Automatically build nested field, ruby on rails - ruby-on-rails

Now I'm making rails app for survey. I like to build answer fields for every questions automatically. But I can't make it.
My model
class Survey
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
class Question
hasmany :answers
end
class Answers
belongs_to :survey
belongs_to :question
end
My controller
class SurveyController
def new
#survey = Survey.new
question_length = Questions.length
question_length.times{#survey.answers.build}
end
end
My view
= simple_form_for(#survey) do |f|
= f.input :input_date, as: :date
= f.simple_fields_for :answers do |ff|
= ff.input :question_id, collection: questions.all.map{ |q| [q.title, q.id] }
= ff.input :answer_text
It's working now like this, and answer fields are built for every questions. But it makes user to select question. So I want question_id built automatically for ever questions.
I've tried this controller
def new
#survey = Survey.new
question_length = Questions.length
question_array = Questions.all.map{|q| q.id}
question_length.times{#survey.answers.build(question_id: question_array)}
end
But it doesn't work. If somebody knows how to make it, please advise me.
Regards

I got answer.
class SurveyController
def new
#survey = Survey.new
#survey.answers.build
end
end
- #questions.each do |questions|
= simple_form_for(#survey) do |f|
= f.input :input_date, as: :date
= f.simple_fields_for :answers do |ff|
%p= questions.title
= ff.hidden_field :question_id, value: question.id
= ff.input :answer_text
It's not perfect answer. But it's fine. Thanks watching my question.

Related

build method not working for multiple fields on rails

I have two models.
Question.rb
Class Question
belongs_to :quiz
has_many :possible_answers
end
PossibleAnswer.rb
Class possible_answer
belongs_to :question
end
I am trying to add multiple possible answers to a question by doing these changes to questions controller and form.
questions_controller.rb
def new
#question = #quiz.questions.build
5.times { #question.possible_answers.build }
end
_form.html.erb
<p>
<label>Specify some choices:</label>
</p>
<%= f.fields_for :possible_answers do |c| %>
<p>
<%= c.text_field :title, placeholder: "Type your choice", class: "form-control" %>
</p>
<% end %>
By what I read it should give 5 fields to enter possible answers, but instead still giving single field. Can anybody please help me out here ?
I think you must do it this way:
def new
#question = #quiz.questions.build
5.times { #question.possible_answers << Possible_answer.build }
end

save multiple columns in one form

I want a User to be able to answer all questions that are assigned to them, in an Answer model. Now I'm trying to create a form that allows me to loop through the questions a User have assigned to them, and answer them in an Answer model.
In the answer model I save the reply, and the question id. However this requires multiple saves in one form, which I'm unable to do.
Model associations look like this:
User
has_many :answers
has_many :questions, through: :question_participants
Answer
belongs_to :user
belongs_to :question
now I'm trying to create an Answer#new form like this:
<%= form_for #answer do |f| %>
<% #questions.each do |question| %>
<h3><%= question.name %></h3>
<%= f.hidden_field :question_id, value: question.id %>
<%= f.text_field :reply, class: 'form-control' %>
<% end %>
<%= f.submit 'Send inn', class: 'btn btn-success' %>
<% end %>
and thus hoping it will allow me to save multiple columns in one, but that doesn't work. It only saves the last column, no matter what.
My answers controller:
class AnswersController < ApplicationController
def new
#questions = current_user.questions
#answer = current_user.answers.new
end
def create
#questions = current_user.questions
#answer = current_user.answers.new(answer_params)
if #answer.save
redirect_to answers_path
else
render 'new'
end
end
private
def answer_params
params.require(:answer).permit(:reply, :question_id)
end
end
What you're looking for is accepts_nested_attributes_for:
This should work:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :answers
has_many :questions, through: :answers
accepts_nested_attributes_for :answers
#this will have to be populated on user create
before_create :build_answers
private
def build_answers
questions = Question.find [1,3,4,6]
questions.each do |question|
user.build_answer(question: question)
end
end
end
#app/models/answer.rb
class Answer < ActiveRecord::Base
#columns id | user_id | question_id | response | created_at | updated_at
belongs_to :user
belongs_to :question
end
#app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers
has_many :users, through: :answers
end
This will give you the ability to do the following:
#config/routes.rb
resources :answers, only: [:edit, :update]
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def edit
#questions = current_user.questions
end
def update
#answers = current_user.answers.update answer_params
end
private
def answer_params
params.require(:answer).permit(:response) #-> question_id and user_id set on create, don't need to be changed
end
end
This will allow you to use the following form:
#app/views/answers/edit.html.erb
<%= form_tag answers_update_path, method: :patch do |f| %>
<% #questions.each do |question| %>
<%= f.fields_for "answers[]", question do |qf| %>
<%= qf.label question.title %>
<%= qf.text_field :response %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
--
Typically, you'd use the accepts_nested_attributes_for with the nested model. However, since you just want multiple answer responses, you can use the above.
The bugs in this would likely be in the strong params, or in the form declaration (IE current_user.questions). If you reply with information, I'll write some upates
Ref: Multiple objects in a Rails form

How to eager load a fields_for with validation

I have this fields_for in my form:
= f.fields_for :parts do |builder|
= builder.input :body, label: builder.object.another_relation.description, required: false
In the main model I have this validation:
validates_associated :parts
In the parts model I have this validation:
validates :body, presence: true
This works well. When I leave some part empty and try to create the model, the form is re-displayed again with proper error messages.
But in the logs I see that the label is querying the database for each part, so I need to eager load this.
How?
This was my try:
= f.fields_for :parts, f.object.parts.includes(:question_part) do |builder|
= builder.input :body, label: builder.object.another_relation.description, required: false
This eager loads the relationship successfully, but when I leave some field empty and try to create the model, the form is re-displayed with all parts empty and no errors. I think this is normal, as I'm forcing the fields_for to load fields from that collection I'm passing.
Anyone knows how to solve this?
Here's a way to do it.
Imagine these 4 models, related to a question-answers site like StackOverflow, so everyone knows what we are talking about :)
So, a question has N parts, and also N answers. While an answer belongs to a question, and has N parts. Finally, an answer part belongs to a question part.
class Question
has_many :parts, class_name: 'QuestionPart', dependent: :destroy
accepts_nested_attributes_for :parts, allow_destroy: true
has_many :answers, dependent: :destroy
# ...
end
class QuestionPart
belongs_to :question
# ...
end
class Answer
belongs_to :question
belongs_to :user
has_many :parts, class_name: 'AnswerPart', dependent: :destroy
accepts_nested_attributes_for :parts, allow_destroy: true
# ...
end
class AnswerPart
belongs_to :answer
belongs_to :question_part
validates :body, presence: true
# ...
end
Let's take a look at the answer controller:
class AnswersController < ApplicationController
def new
#question = Question.find(params[:question_id)
#answer = #question.answers.new
#question.parts.each { |p| #answer.parts.build(question_part: p) }
end
def edit
#answer = Answer.find(params[:id])
#answer_parts = #answer.parts.includes(:question_part)
end
def create
#question = Question.find(params[:question_id])
#answer = current_user.answers.build(answer_params.merge(question: #question))
if #answer.save
redirect_to #question, notice: 'The answer was successfully created.'
else
render :new
end
end
def update
##answer = Answer.find(params[:id])
#answer = Answer.where(id: params[:id]).includes(parts: [:question_part]).first
if #answer.update(answer_params)
redirect_to #answer.question, notice: 'The answer was successfully updated.'
else
render :edit
end
end
private
def answer_params
params.require(:answer).permit(:body, parts_attributes: [:id, :question_part_id, :body])
end
end
Please notice how I retrieve the answer in the update method. I have commented out the standard way, while I'm retrieving it with a custom query (which should be moved into the Answer model). Here is where I had trouble. If anyone knows a better way, just comment or add a new answer.
Now for the templates:
# new.html.erb
...
<%= form_for [#question, #answer] do |f|
<%= render 'form', f: f, parts: #answer.parts
<% end %>
# edit.html.erb
...
<%= form_for [#answer] do |f|
<%= render 'form', f: f, parts: #answer_parts || #answer.parts
<% end %>
# _form.html.erb
...
<%= f.fields_for :parts, parts do |builder| %>
<%= builder.text_field :body, label: builder.object.question_part.description, required: false
<%= builder.hidden_field :question_part_id %>
<% end %>

Deep Nested Rails 4 Form

I have 3 models Item which accepts nested attributes for questions and questions accept nested attributes for answers. I'm trying to create an item which has a question and an answer in the same form.
item.rb
class Item < ActiveRecord::Base
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions
end
question.rb
class Question < ActiveRecord::Base
belongs_to :item
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers
end
answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
item_controller.rb
class ItemsController < ApplicationController
def new
#item = #repository.items.new
questions = #item.questions.build
answers = questions.answers.build
end
def create
#item = Item.new(item_params)
if #item.save
redirect_to #item, notice: '...'
else
render action: 'new'
end
end
private
def item_params
params.require(:item).permit(:id, :content, :kind, :questions_attributes => [:content, :helper_text, :kind], :answers_attributes => [:content, :correct])
end
end
_form.haml
= simple_form_for(#item) do |f|
= f.input :kind
= f.input :content
= f.simple_fields_for :questions do |q|
= q.input :content
= q.simple_fields_for :answers do |a|
= a.input :content
= f.submit
The form is being displayed correctly and it saves the question model correctly. I can't seem to save the answer though.
I've already looked at a lot of online help but none are covering it with Rails 4 strong params.
I think your problem is with your strong params:
def item_params
params.require(:item).permit(:id, :content, :kind, questions_attributes: [:content, :helper_text, :kind, answers_attributes: [:content, :correct]])
end
Basically, when you pass a deep nested form (where you have multiple dependent models), you'll have to pass the attributes as part of the other model's attributes. You had the params as separate
I run into a similar issue and, while Richard Peck answer helped me as well, there is one thing that it was missing for me.
If you are deep-nesting you need to specify the id of the parent of the nested item. In this case to create an answers you need to make questions id explicit with q.input :id, otherwise you will run into this error.
= simple_form_for(#item) do |f|
= ...
= f.simple_fields_for :questions do |q|
= ...
= q.input :id
= q.simple_fields_for :answers do |a|
= ...
= f.submit

nested attributes, passing current_user.id to nested model

I have 3 models: User, Answer and Question.
user.rb
has_many :questions
has_many :answers
question.rb
has_many :answers
belongs_to :user
accept_nested_attributes_for :answers
answer.rb
belongs_to :question
belongs_to :user
In questions/show.html.erb
form_for #question do |f|
f.fields_for :answers, #question.answers.build do |builder|
builder.text_area, :body
end
f.submit
end
Submit calls the questions#update action and, thanks to nested resources, will the new answer be saved in the database. I wonder: how can I save the user_id column for answer, in the database, after the question is submitted? Can I somehow pass current_user.id to answer's user_id column after submitting the form?
You have to pass the user parameter in your controller's create action (also you should build your nested attributes in the controller):
def new
#question = Question.new
#question.answers.build
end
def create
#question = current_user.questions.new(params[:questions])
//The line below is what will save the current user to the answers table
#question.answers.first.user = current_user
....
end
Your view's form should therefore look like:
form_for #question do |f|
f.fields_for :answers do |builder|
builder.text_area, :body
end
f.submit
end
You could do something like this in your controller's update action:
# questions_controller
def update
params[:question][:answers_attributes].each do |answer_attribute|
answer_attribute.merge!(:user_id => current_user.id)
end
if #question.update_attributes(params[:question])
...
end
end
Another more simple solution would be to add the user_id to your form like this:
form_for #question do |f|
f.fields_for :answers, #question.answers.build(:user_id => current_user.id) do |builder|
builder.text_area, :body
builder.hidden_field :user_id # This will hold the the user id of the current_user if the question is new
end
f.submit
end
The problem with this approach is that users would be able to edit the value through a HTML source code inspector (like in chrome), and thereby set the user to someone else. You could of course validate this in some way, but that too would be a bit complex.
'current_user' is a devise helper method. it can be accessed in controllers n views directly.
if i m not wrong, only the new answer should have the current_user.id Old answers should not be updated. u can do this as
f.fields_for :answers, #question.answers.build do |a|
a.hidden_field :user_id, :value => current_user.id
a.submit

Resources