2
votes

I've been trying to use nested forms or simple forms in my Rails app, specifically in my questions/show.html.erb, but when I click submit it will give me something strange as parameters.

I have Questions that have many Answers. I want to be able to create an answer in the questions show view.

Here is my code:

QuestionsController:

def new
 @question = Question.new
 @question.answers.build
end

def create
 @question = Question.new(question_params)
 if @question.save
  redirect_to question_path
 else
  render 'new'
 end
end

def show
  @question = Question.find(params[:id])
  @question.answers.build
end

def edit
  @question = Question.find(params[:id])
  @question.answers.build
end

def update
  @question = Question.find(params[:id])
  if @question.update(question_params)
    redirect_to question_path
  else
    render :edit
  end
end

def question_params
 params.require(:question).permit(:id, :question_title, :question_text, answers_attributes: [ :id, :answer_text, :question_id, :_destroy])
end

AnswersController.rb

def new
  @answer = Answer.new
end

def create
  @answer = Answer.new(@answer_params)
  @answer.question = Question.find(params[:question_id])
  @answer.save
  respond_to do |format|
    format.js
  end
end

def edit
  @answer = Answer.find(params[:id])
end

def update
  @answer = Answer.find(params[:id])
  if @answer.update(answers_params)
    redirect_to answers_path
  else
    render :edit
  end
end

Question.rb

class Question < ApplicationRecord
  belongs_to :topic
  belongs_to :user
  has_many :answers, dependent: :destroy
  accepts_nested_attributes_for :answers, allow_destroy: true
end

Answer.rb

class Answer < ApplicationRecord
  belongs_to :question
end

questions/show.html.erb

<div class=question-show> 
  <div class="question-title"> <%= @question.question_title %></div>
  <div class="question_text"> <%= @question.question_text %></div>
  <div class="question-user"><%= question_show_timestamp(@question) %> </div>
  <div class="question-topic"> <strong>Category:</strong> <%= @question.topic.category.category_name %></div>
  <div class="question-topic"> <strong>Topic:</strong> <%= @question.topic.topic_name %></div>
  <div class="question-answers">
    <%= render "form" %>
    <hr>
    <% @question.answers.select(&:persisted?).each do |answer| %>
      <div> 
        <div> <%= answer.answer_text %> </div>
        <div class="small-font"> <%= answer_timestamp(answer) %> by <%= answer.user.user_name %> </div>
      </div>
      <hr>
    <% end %>
  </div>
</div>

_form.html.erb

<%= simple_form_for @question do |form| %>
  <div class="TabbedPanelsContent">
    <%= form.simple_fields_for(:answers, @question.answers.build) do |builder| %>
      <%= render "answer_fields", form: builder %>
    <% end %>
    <p><%= link_to_add_fields "Add answer", form, :answers %></p>
  </div>
  <%= form.submit "Save" %>
<% end %>

Please note that i'm using

simple_fields_for(:answers, @question.answers.build)

If I wrote it as simple_fields_for :answers, it would render one field for each answer that the question has, I believe it is because I am using this simple_form in the "show.html.erb" view, but i'm not completely sure. Any comments on that as well would be very helpful!

_answer_fields.html.erb

<div class="fields">
  <p>
    <%= form.input_field :_destroy, as: :hidden %>
    <%= link_to "Remove", "#", class: "remove_record" %>
  </p>
  <p class="lable">Answer</p>
  <p class="field"><%= form.text_field :answer_text %></p></br>
</div>

this is what happens when I click save:

Started PATCH "/questions/19" for 172.18.0.1 at 2018-04-02 13:15:06 +0000
Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by QuestionsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"yhLrwHi8wgPRx30ysPakqcKfb+Pjd3BhWN4F/7K5m+FaraCpi7iq6RcrT98q/ISGTv/g8ZlcrDX1ItnrRTBgfQ==", "question"=>{"answers_attributes"=>{"0"=>{"_destroy"=>"false", "answer_text"=>"wer"}}}, "commit"=>"Save", "id"=>"19"}
  Question Load (0.8ms)  SELECT  "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT $2  [["id", 19], ["LIMIT", 1]]
   (0.4ms)  BEGIN
  Topic Load (0.8ms)  SELECT  "topics".* FROM "topics" WHERE "topics"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.5ms)  ROLLBACK
  Rendering questions/edit.html.erb within layouts/application
  Rendered questions/edit.html.erb within layouts/application (0.6ms)
  User Load (0.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 2], ["LIMIT", 1]]
  Category Load (0.8ms)  SELECT "categories".* FROM "categories"
  Rendered layouts/_navbar.html.erb (7.2ms)
  Rendered questions/_new.html.erb (3.2ms)
Completed 200 OK in 416ms (Views: 392.0ms | ActiveRecord: 4.8ms)

The line that looks funny to me is:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"yhLrwHi8wgPRx30ysPakqcKfb+Pjd3BhWN4F/7K5m+FaraCpi7iq6RcrT98q/ISGTv/g8ZlcrDX1ItnrRTBgfQ==", "question"=>{"answers_attributes"=>{"0"=>{"_destroy"=>"false", "answer_text"=>"wer"}}}, "commit"=>"Save", "id"=>"19"}

As you can see, in "answers_attributes" => there is n extra "0" there. Am i doing something wrong? Any advice or help would be greatly appreciated.

1
Can you show how params.require been implemented? - Michael Arkhipov
Everything is alright. This is how accepts_nested_attributes_for works. Now when a question is created, associated answers are created too. Show the QuestionsController#question_params method. - chumakoff
I added the questions params on the QuestionController, as you requested. Thank you !! - Erik Torres
There's nothing wrong on showing a 0 on answers attributes, since you can have N answers for a question. What's your really problem here? Aren't the answers being persisted on your database? - Morris
question_params looks fine. You need to find out why your records are not saved. Do you see any validation errors in the form after the page is reloaded? You should debug @answer after trying to update it in QuestionsController#update method. - chumakoff

1 Answers

0
votes

That parameters line seems to you strange, but actually it's the way how nested forms for has_many relations work, so mostly you implemented everything right, but I think you don't need a nested form here.

You would need nested form if you wanted to create or update the question AND one or many answers belonging to it AT ONCE, with one request.

Here you want only to create an answer, you don't want to touch question. So you can simplify the whole thing - only create form for an answer.

questions_controller.rb:

def show
  @question = Question.find(params[:id])
  @answer = @question.answers.build
end

Since you respond with js in AnswersController, I assume, that your form is supposed to be remote.

questions/show.html.erb:

<div class="question-answers">
  <%= simple_form_for @answer, remote: true do |f| %>
    <%= f.hidden_field @answer.question_id $>
    <%= render 'answers/form_fields', f: f %>
    <%= f.submit "Save" %>
</div>

(don't forget to add question_id to permitted params in AnswersController)

A good thing would be to move form fields for answer to answers views folder.

answers/_form_fields.html.erb:

<div class="fields">
  <p class="lable">Answer</p>
  <p class="field">
    <%= f.text_field :answer_text %>
  </p>
  </br>
</div>

After that you should see the next in the log when you submit (if everything is ok with your routes):

Processing by AnswersController#create

Then I would change your AnswersController a bit:

def create
  @answer = Answer.create(@answer_params)
end

No need to explicitly set the question, because now you set it through params. No need to specify format if you respond to only one format.