I'm trying to make a dynamic form of questions and answers, like so:
Question _______
Answer _______
Question _______
Answer _______
I can't figure out how to loop through the two resources as alternating pairs. I have tried this:
<%= semantic_fields_for [@question, @answer] do |h, i| %>
<%= f.inputs :for => @question do |h|%>
<%= h.input :question %>
<% end %>
<%= f.inputs :for => @answer do |i|%>
<%= i.input :answer %>
<% end %>
<% end %>
But it gives me the error "Undefined method `model_name' for Array:Class."
My controller:
def new
@post = Post.new
@question = @post.questions.new
@answer = @question.build_answer
respond_to do |format|
format.html
end
end
And my models:
class Post < ActiveRecord::Base
has_many :questions
has_many :answers
end
class Question < ActiveRecord::Base
belongs_to :post
has_one :answer
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :post
end
So I don't personally use formtastic but I understand it follows similar lines to simple_form. Your error is coming from trying to pass an Array to semantic_fields_for which only takes a single object:
<%= semantic_form_for @questions do |q| %>
<%= q.input :question %>
<%= q.semantic_fields_for @answer do |a| %>
<%= a.inputs :answer %>
<% end %>
<%= q.actions %>
<% end %>
Don’t forget your models need to be setup correctly with accepts_nested_attributes_for
class Question < ActiveRecord::Base
belongs_to :post
has_one :answer
accepts_nested_attributes_for :answers
end
You'll want to check out the formtastic docs at https://github.com/justinfrench/formtastic
That should get your form showing correctly in the view but you'll need to add some more to your questions controller to make sure it saves the answers (someone correct me if I'm mistaken).
Also just so it's clear do your Questions and Answers tables really have a question and answer column? If the columns are actually something like :body you'll want to replace the relevant symbols in the above code.
I think what you need is exactly what is described in these railcasts:
- Nested Model Form Part 1
- Nested Model Form Part 2
I think you should also refactor a bit, Posts should not have questions. You might notice a little difference from the railcasts but that's because you have only one answer per question whereas in the railcasts a question has many answers. In the part 2 it shows how to add AJAX calls to add/remove questions and answers (probably you won't need this if you only have one answer).
Mandatory reading so you have a better understanding of associations and how nested attributes work:
- A Guide to Active Record Associations
- Active Record Nested Attributes
And this is an example that will probably work, with some minimum tweaking. I haven't used semantic fields, just the standard form builder.
class Post < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class Question < ActiveRecord::Base
belongs_to :post
has_one :answer, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class Answer < ActiveRecord::Base
belongs_to :question
end
# posts_controller.rb
def new
@post = Post.new
# lets add 2 questions
2.times do
question = @post.questions.build
question.build_answer
respond_to do |format|
format.html
end
end
# views/posts/_form.html.erb
<%= form_for @post do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |builder| %>
<%= render "question_fields", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
# views/posts/_question_fields.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question" %>
</p>
<%= f.fields_for :answers do |builder| %>
<%= render 'answer_fields', :f => builder %>
<% end %>
# views/posts/_answer_fields.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
</p>