Adding a list item client-side
I'll say right off the bat that I'm a complete beginner to Rails, so this might be something obvious that I'm missing.
I have a web form for editing an unordered (<ul>
) list of questions. Each question is an item in the list. Each question has a text field for the question text and a dropdown to indicate what type of question it is (multiple choice, true/false, etc).
I want to also give the user the ability to add new questions, by clicking a link and having a new text box and dropdown appear, as items on the list.
I haven't found a really clean way to do that, so I'm sort of piecing it together from various tutorials. Is there a better approach I'm missing?
Here's the code for edit.html.erb:
<h1>Editing Survey: <%= "#{@survey.name}" -%></h1>
<% form_for @survey, :url => {:action => 'update', :id => params[:id]} do |survey_form| %>
<h2>Title: <%= survey_form.text_field :name %></h2>
<ol class='question_list'>
<% for @question in @survey.questions %>
<% fields_for "question[]" do |f| %>
<li>
<%= f.text_field :q_text %><br>
<%= select(:question, :q_type, { "True/False" => "T", "Multiple Choice" => "M"}) %><br>
</li>
<% end %>
<% end %>
</ol>
<%=
link_to_function "Add another question",
update_page { |page|
page.insert_html :bottom,
'blank_question',
(r开发者_如何学运维ender :partial => 'blank_question', :object => @question )
}
%>
<%= submit_tag "Update" %><br>
<% end %> <% "End of form_for" %>
<%= link_to "Back", {:action => 'list' } %>
I created another erb file called _blank_question.erb, which is the elements I would like to have included when they click on the "Add another question" link:
<li>
<%= text_field :question, :id, :size => 25 %><br>
<%= select(:question, :q_type, { "True/False" => "T", "Multiple Choice" => "M"}) %><br>
</li>
The problem is that I get the following error in a popup when I click on the "Add another question" link.
RJS error:
TypeError: Object function Element() { [native code] } has no method 'insert'[native code] } has no method 'insert'
Quite a bit of googling hasn't turned up any helpful results. Any idea where I'm going wrong here?
If you are using Rails version > 2.3 (which I recommend if you don't), you can watch two screencasts:
- Nested Model Form Part 1
- Nested Model Form Part 2
that explain exactly what you want to do. The second part deals with the Ajax part, but I really recommend watching both the first and the second parts, because Ryan Bates builds this gradually and it will really help you clarify a lot of things.
Generally, the Railscasts site is one of the best ways to learn about a lot of simple or complex Rails stuff.
Some hints:
There seems to be no element 'blank_question' where the rendered HTML could be inserted. You may want to insert into a parent element:
update_page do |page|
page['parent'].insert_html :bottom, :partial => 'blank_question',
:object => @survey.questions.build
end
Or even simpler let Rails decide which partial to use:
update_page {|page| page['parent'].insert_html :bottom, @survey.questions.build }
This will try to use "_question.html.erb" passing an empty question already associated with your survey as local variable "question", plus you can share partials for rendering existing and new questions (see below).
BTW: See how you can directly skip the call to render (and let Rails build the empty question for your survey).
Another BTW: You can pass the collection to fields_for
like so (and it will automatically loop over it and you can skip the for loop):
survey_form.fields_for :questions do |f|
#...
end
However, this only works if your class Survey; has_many :questions
. But this is probably the case. fields_for
used on the parent form also supports new objects not yet in the database - this may be of use for you (aka "blank_question"). Also, you can just use form_for @survey do |survey_form|
. Rails will be smart enough to use update or create URLs depending on the object being in the database or not. Rails uses the new_record?
method for this.
Addendum to the above - to share partials then use:
survey_form.fields_for :questions {|f| render f.object }
This will render "_question.html.erb" for each collection member.
I typically use a helper to render such nested forms:
module SurveysHelper
def setup_survey(s)
# add empty question to survey if none associated
s.questions.build unless s.questions.size > 0
return s
end
end
and in the view:
form_for setup_survey(@survey) do |survey|
survey.fields_for :questions {|f| render f.object }
end
Make sure that prototype.js is included in your rails' javascript directory and that you are importing it in your header:
精彩评论