Handling radio buttons when applying to a nested form created dynamically - ruby-on-rails

I have an app where users can create their own forms and then apply them to their own clients
The forms are created dynamically, meaning users can add as many questions they want, and each question has many choices (think of a survey structure).
So far so good, now I'm struggling when applying the forms, specifically getting the answers saved, persisted and showing properly when rendering the edit view.
When rendering the edit view, each question has their choices multiplied by how many questions/answers the answered_form has (I don't know exactly which of them, I am guessing here).
Answers are persisted to database, although each question has their choices multiplied, the selected answer is checked and is checked in the collection of the original answer. (answer one checked in the first 3 choices, answer two in the second 3 choices, answer three in the third 3 choices and answer four in the forth 3 choices)
I've read 2 similar questions here in SO, RoR nested attributes produces duplicates when edit, and Nested form update action producing duplicate results, but I already have the :id in strong parameters (you can see it in the code below).
I want to make myself clear here: creating the form, with nested question and nested choices is working perfectly fine, and also editing the created form. The struggle is when USING, ANSWERING or APPLYING it in the APP.
Code:
_form.html.erb:
<%= form_for [#child, #answered_form] do |f| %>
<%= f.hidden_field :form_id %>
<%= f.hidden_field :child_id %>
<div class="answered_form">
<h1><%= #form.f_title %></h1>
<h3><%= #form.f_description %></h3>
<br>
<% questions = #form.questions %>
<% i = 1 %>
<% questions.each do |question| %>
<%= i.to_s + ". " %><%= question.q_title %>
<br />
<% choices = question.choices %>
<%= f.fields_for :answers do |a| %>
<% choices.each do |choice| %>
<%= a.radio_button :a_content, choice.c_description %>
<%= a.label :a_content, choice.c_description, :value => choice.c_description, class: 'no-margin' %>
<br />
<% end %>
<% end %>
<br />
<% i += 1 %>
<% end %>
</div>
<div class="text-center">
<%= f.submit yield(:button_text), class: "btn btn-primary btn-lg" %>
</div>
<% end %>
answered_forms_controller.rb:
class AnsweredFormsController < ApplicationController
before_action :correct_answered_form, only: [:edit, :update, :destroy]
def new
#child = current_user.children.find(params[:child_id])
#form = current_user.forms.find(params[:form_id])
#answered_form = #child.answered_forms.new(form_id: params[:form_id])
#answered_form.answers.build
end
def create
#answered_form = AnsweredForm.create(answered_form_params)
if #answered_form.save
flash[:success] = "New survey " + #answered_form.form.f_title + " applied to patient!"
redirect_to current_user.children.find(params[:child_id])
else
render 'new'
end
end
def edit
#child = current_user.children.find(params[:child_id])
#form = current_user.forms.find(params[:form_id])
end
def update
if #answered_form.update_attributes(answered_form_params)
flash[:success] = "Survey updated!"
redirect_to #answered_form.child
else
render 'edit'
end
end
def show
end
def destroy
#child = current_user.children.find(params[:child_id])
#form = current_user.forms.find(params[:form_id])
#answered_form.destroy
redirect_to :back
end
private
# Strong params for creating and updating forms
def answered_form_params
params.require(:answered_form).permit(:form_id, :child_id, answers_attributes: [:id, :a_content, :a_boolean, :_destroy, :choice_id])
end
# Confirms the correct answered_form
def correct_answered_form
#answered_form = AnsweredForm.find(params[:id])
end
end
Logs:
POST:
Started POST "/children/1-juan-gomez-pereira/answered_forms" for ::1 at 2016-07-08 11:55:01 -0400
Processing by AnsweredFormsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hFrRfEwVG4XSkdbPwrohm1QEQ0FtE/as3sM2Fj3Av3reVHJxZBVKPeuAeD713H7gVyZn7eppnULDhLJQz+EBeg==", "answered_form"=>{"form_id"=>"1", "child_id"=>"1", "answers_attributes"=>{"0"=>{"a_content"=>"Bajo"}, "1"=>{"a_content"=>"Sí"}, "2"=>{"a_content"=>"Derecha"}, "3"=>{"a_content"=>"Pesado"}}}, "commit"=>"Aplicar", "child_id"=>"1-juan-gomez-pereira"}
PATCH:
Started PATCH "/children/1-juan-gomez-pereira/answered_forms/3" for ::1 at 2016-07-08 11:55:54 -0400
Processing by AnsweredFormsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oGdqgUR95HMkMff40Gq1zWar/CH64F0jsN3oRHT1q/H6acmMbH21yx0gWQnnDOq2ZYnYjX2aNs2tmmwChtQV8Q==", "answered_form"=>{"form_id"=>"1", "child_id"=>"1", "answers_attributes"=>{"0"=>{"a_content"=>"Bajo", "id"=>"9"}, "1"=>{"a_content"=>"Bajo", "id"=>"10"}, "2"=>{"a_content"=>"Bajo", "id"=>"11"}, "3"=>{"a_content"=>"Confuso", "id"=>"12"}, "4"=>{"a_content"=>"No", "id"=>"9"}, "5"=>{"a_content"=>"Sí", "id"=>"10"}, "6"=>{"a_content"=>"Confuso", "id"=>"11"}, "7"=>{"a_content"=>"Confuso", "id"=>"12"}, "8"=>{"a_content"=>"Confuso", "id"=>"9"}, "9"=>{"a_content"=>"Izquierda", "id"=>"10"}, "10"=>{"a_content"=>"Confuso", "id"=>"11"}, "11"=>{"a_content"=>"Izquierda", "id"=>"12"}, "12"=>{"a_content"=>"Liviano", "id"=>"9"}, "13"=>{"a_content"=>"Liviano", "id"=>"10"}, "14"=>{"a_content"=>"Pesado", "id"=>"11"}, "15"=>{"a_content"=>"Liviano", "id"=>"12"}}}, "commit"=>"Actualizar", "child_id"=>"1-juan-gomez-pereira", "id"=>"3"}
I noticed that when POST, the id for answer is not passed as a parameter. If I check the console, I can see that answer was created properly.

I think this line could be the problem
<% choices.each do |choice| %>
If you have built question.answers, you can iterate on them only using f.fields_for :answers and skip the above line. I assume choices are not much different from answers.
Please clear me if i am wrong.

So the problem was in this line of the code:
<%= f.fields_for :answers do |a| %>
fields_for helper generates fields for the entire collection of the object for whom your are calling it.
In this case, #answered_form had 4 answers after creating it, so when calling fields_for in edit, it was generating 4 fields set for each question.
To solve this, you need to specify a collection that fits your needs and pass it through the fields_for helper:
<%= f.fields_for :answers, ANSWERS_COLLECTION do |a| %>

Related

Rails 4 associate comment with blog

I have a rails app with a Blog and comments, each blog post has many comments. In each blog (show action) I can submit a comment on at form. My question is I need to associate the blog_id in comments with the blog I am viewing, I could pass this as a hidden view but I am asking for the BEST way to do this, maybe a helper I am unaware of.
<h3>Leave a reply</h3>
<% #blog.comments.each do |comment| %>
<p>
<%= comment.text %>
</p>
<% end %>
<%= form_for(Comment.new) do |f| %>
<%= f.text_field :name %>
<%= f.text_area :text %>
<%= f.hidden_field :blog %>
<%= f.submit %>
<% end %>
When you initialize a new comment, initialize it through a blog instance.
def show
#blog = Blog.find(params[:id])
#comment = #blog.comments.build
end
Then, in your form, you want to use the comment instance instead of initializing a new comment:
<%= form_for(#comment) do |f| %>
Assuming you have the correct relationships the comment will automatically have the blog id.
In the create action you will want to ensure the comment is also created through the blog instance.
def create
#blog = Blog.find(params[:id])
#comment = #blog.comments.build(comment_params)
if #comment.save
# etc ...
end
Although associating a comment with a blog may not be a good design decision depending on what you want to do, unless by blog you mean a post.

RoR creating a form and passing back a hash

thanks in advance for looking at my issue. I'm new to programming and struggling with the concept of saving data to "foreign tables".
So this is a basic survey. I have a list of questions, that have answers belonging to it.
Here is my code that iterates through all the questions and answers:
<% #questions.each do |question| %>
<ul><%= question.questiondescription %></ul>
<% #answers = question.answers %>
<% #answers.each do |answer| %>
<li><%= answer.answerdescription %></li>
<% end %>
<% end %>
This works great. However I want all the answers to be radio buttons and store the selections in another table. I have another controller and table called "assessment_results". It looks like this:
User_Id, Assessment_Id, Question_ID, Answer_Id
I need to create a new record in this table for each question and answer. How would I go about doing this with having all the questions listed on one page?
So my thinking is I need a hash to push into that table for every question.
{[user_id:1, assessment_id:, question_id:1, answer_id:3]}
You could use the radio_button_tag and parse the results yourself.
Here is the breakdown
Looking at your assessment_results table I assume you have an Assessment controller based on the 'assessment_id' column.
Lets use that to present the questions.
Here is the assessments/new.html.erb
<h1>New assessment</h1>
<%= form_for(#assessment) do |f| %>
<% if #assessment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#assessment.errors.count, "error") %> prohibited this assessment from being saved:</h2>
<ul>
<% #assessment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<% #questions.each do |question| %>
<%= question.questiondescription %>
<ul>
<% #answers = question.answers %>
<% #answers.each do |answer| %>
<li><%= answer.answerdescription %><%= radio_button_tag("questions[#{question.id}]", answer.id) %></li>
<% end %>
</ul>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
So we have a standard assessment form that really just gives us the form structure and assessment button, we aren't going to actually use anything from it.
Best to build the view and then look at the source code in a browser.
The work is being done by the radio_button_tag, the first value "questions[#{question.id}]"
Will produce radio buttons with a name of "questions[1]" for the first group of questions and the value will be the answer id.
When rails sees a parameter like questions[1], it will put this into a hash called questions.
Here is the raw hash from the log when submitting the form with answers filled out:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lwqT5y+xUaYvt/GBObpN+BBO3F4YO8XaEGWcpS84lVw=", "questions"=>{"1"=>"2", "2"=>"8", "3"=>"13", "4"=>"18", "5"=>"23"}, "commit"=>"Create Assessment"}
The questions hash is:
"questions"=>{"1"=>"2", "2"=>"8", "3"=>"13", "4"=>"18", "5"=>"23"}
Now in the assessments controller, for the create method, I did the following:
def create
#assessment = Assessment.new(user_id: current_user.id)
respond_to do |format|
if #assessment.save
params[:questions].each do |question, answer|
AssessmentResult.create(user_id: current_user.id, assessment_id: #assessment.id,
question_id: question, answer_id: answer)
end
format.html { redirect_to #assessment, notice: 'Assessment was successfully created.' }
format.json { render :show, status: :created, location: #assessment }
else
format.html { render :new }
format.json { render json: #assessment.errors, status: :unprocessable_entity }
end
end
end
First we are creating an assessment that belongs to the user, I am assuming that you are using Devise or a similar system that uses the current_user for the current logged in user.
Next after saving with #assessment.save, this gives us our #assessment.id that the assessment results will belong to.
So the core work is happening here:
params[:questions].each do |question, answer|
AssessmentResult.create(user_id: current_user.id, assessment_id: #assessment.id
question_id: question, answer_id: answer)
end
end
params[:questions] - this is the params question hash that was submitted by the form.
params[:questions].each do |question, answer| - this takes the hash and for each, entry splits it into the key and value with |key, value|, in this case the key was the question id and the value was the chosen answer id.
Now we store the answer in the AssessmentResult model/table with the create command.
Say you now want to display the results of the assessment with: /assessments/1/show
def show
#assessment_results = AssessmentResult.where(assessment_id: #assessment.id)
end
Now you have the question and selected answer in the AssessmentResult
Make sure you set up your relations in the model:
app/models/assessment_result.rb
class AssessmentResult < ActiveRecord::Base
belongs_to :question
belongs_to :answer
end
Simple display of quesiton and selected answer, you will have to add the 'correct answer' code yourself.
app/views/assessments/show.html.erb
<% #assessment_results.each do |ar| %>
<%= ar.question.questiondescription %>: <%= ar.answer.answerdescription %>
<br />
<% end %>
Ok, when you have a nested models, here's what you can do
<%= form_for(#question) do |f| %>
<%= f.fields_for :answer do |ff| %>
<%= ff.label :image, 'Tag:' %>
<%= ff.checkbox :description, multiple: true, name: "answer[description]" %>
In your case, you have several answers belonging to some question, the principle is the same except you have to build specific ids for each answer, so I strongly suggest you these very helpful railscast
http://railscasts.com/episodes/197-nested-model-form-part-2
http://railscasts.com/episodes/75-complex-forms-part-3

Parameters not being read even though they appear to be passed

I'm making a form that creates more than one record for the user depending on how many items the user decides to check off in the form using checkboxes.
Currently, I'm running into an error where param is missing or the value is empty: itemrecord even though in the log, it appears that params are passing through:
{"utf8"=>"✓", "authenticity_token"=>"m2NMruoFRr6lpsuVMK9UthlY0bsJsPmf1LWce2uKaH4=", ":item_name"=>["Backpack", "Water filter"], "commit"=>"Go!"}
Model relationship is that a User has_many :inventories
Controller code:
def create
#itemrecord = #current_user.inventories.build
items_to_be_saved = []
inventory_params.each do |i|
items_to_be_saved << ({ :signup_id => #current_user.id, :item_name => i })
end
if Inventory.create items_to_be_saved
flash[:success] = "Thanks!"
redirect_to root_path
else
render new_inventory_path
end
end
def inventory_params
params.require(:itemrecord).permit(:item_name)
end
View code:
<%= form_for #itemrecord do |f| %>
<!-- In case you're wondering, the #wishlist below is basically a hash of categories of items and items. This hash is updated in the controller, and then used by multiple views to create the same table of items. -->
<% #wishlist.each do |category, list| %>
<div class="col-xs-2">
<div class="form-group box">
<h5> <%="#{category}"%> </h5>
<% list.each do |thing| %>
<%= check_box_tag ":item_name[]", "#{thing}" %>
</br>
<% end %>
</div>
</div>
<% end %>
<%= f.submit "Go!", class: "btn btn-primary btn-large btn-block" %>
</div>
<% end %>
By the way I also tried changing :item_name to :item_names to account for the array based on what else I read on SO, but that didn't fix it either
Take a look at your inventory_params function. You're saying that you require an itemrecord, and permit an item_name attribute. Observe:
def inventory_params
params.require(:itemrecord).permit(:item_name)
end
However, in the parameters being passed, there is no reference to an itemrecord object whatsoever, but there is a reference to item_name. A quick change to your inventory_params method, removing the :itemrecord requirement and instead requiring :item_name, will fix your issue.
def inventory_params
params.require(:item_name)
end
While this isn't necessarily the best way to go about doing this (I'd suggest reading up on your Active Record Form Helpers), it should solve your issue.

What does the edit/update controller and form view look like for a nested form?

I think my view is being structured incorrectly, but it was the only way I could get all the form fields to appear.
I'm trying to make a for that has two nested models. I know best practice is to have only one nested model, so I can't seem to find the solution for this problem.
My #edit page is only passing a single parameter for the double nested field. As a result, the #update controller is not properly updating the model.
View
## edit.html.erb
<%= form_for :question, url: scenario_question_path(), method: :patch do |f| %>
{{ ...error & non-nested inputs }}
<ol>
<% #question.answers.each do |fa| %>
<%= f.fields_for :answers, fa do |ff| %>
<li>
<%= ff.text_field :answeroption %>
</li>
<% end %>
<% end %>
</ol>
{{ submit }}
Controller
## questions_controller.rb
def update
#scenario = Scenario.find(params[:scenario_id])
#question = #scenario.questions.find(params[:id])
if #question.update(params[:question].permit(:questionprompt, :text, answers: [:answeroption]))
redirect_to scenario_question_path
else
render 'edit'
end
end
def edit
#scenario = Scenario.find(params[:scenario_id])
#question = #scenario.questions.find(params[:id])
#questions = #scenario.questions.all
#answers = #question.answers.all
end
The params being passed when Edit is submitted. The problem is that "answers" actually had more than one changed fields, but only the first one appears.
{"utf 8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"nvydgO4oxCo58y4gmRAJ5P8Kc+kmbqWGoQ0IjIuzYiQ=",
"question"=>{"media"=>"test.jpg",
"questionprompt"=>"123123123",
"answers"=>{"answeroption"=>"2344634"}},
"commit"=>"Save Question",
"scenario_id"=>"1",
"id"=>"1"}
I outputted json and realized the model was structured in a different way than I realized. Also, the view was calling the same field 4 times, rather then calling four different fields.

checkboxtag in forms

Im looking for the following thing: an array of all users (only 6 in this case) with a checkbox in front of their name, resulting in a list of selectable players for the game.
Current code:
<%= form_for #game, url: games_path, :method => "post" do |f| %>
<%= f.label :name %>
<%= f.text_field :name, :value => "#{current_user.name}\'s Game" %>
<%= f.fields_for :participants do |ff| %>
<%= ff.label :user_id %>
<%= ff.text_field :user_id %>
<%= ff.check_box :user_id %>
<% end %>
<%= f.submit "Create Game", class: "btn btn-primary" %>
<% end %>
I'm now having 3.times { #game.participants.build } in my controller which effectively gives me 3 textfields in which i can fill in the participant id in order to make a record in the table participants (which is linked to games).
I've been looking around for 1.5h now and i cant seem to find a proper answer. What i need is a syntax that gives me a list of all current users (say #users) with a checkbox attached to it. When I click the checkbox it should add its id to the parameters and i should be able to create a new game with the linked participant id's. However I'm getting some problems with the ID's attached to the check_box which always seems to be 1. I've read some stuff about checkboxes being a pain with hashes, but I have no other solution atm.
I tried:
<% #users.each do |i| %>
<%= check_box_tag "alternate_numbers[#{i}]" %> <%= i.name %><br />
<% end %>
But i see no way to get that fixed up part of the form itself.
GamesController code (edit):
def new
#users = User.paginate(page: params[:page])
#games = current_user.games
#game = Game.new
3.times { #game.participants.build }
end
def create
#game = Game.new(params[:game])
#newround = #game.rounds.new
#newround.storyFragment = "New story!"
if #game.save && #newround.save
flash[:success] = "Created!"
redirect_to game_path(#game.id)
else
redirect_to root_url
end
end
It's very vague to describe since im not exactly sure how to accomplish this.
In short: the check_box should contain the value of the user_id in the loop. I'm now filling in a manual ID with the text_field helper but i'd like to have the checkbox linked to the username that is right next to it in the view.
Any guidelines/solutions/tips?
Thx
Okay, so you're making a form for a new Game. You now have to feed that new Game, along with some Participants to your view.
def new
#game = Game.new
#participants = User.all # or the users you want
end
Now use those in your view. You were on the right track. Depending on how your create action works:
<% #participants.each do |p| %>
<%= check_box_tag "participants[#{p.id}]" %> <%= p.name %>
<% end %>
I think what you were missing was the documentation for check_box_tag. The input attribute name is the argument.
You also seem to have a lot of logic in your controllers. Remember to keep the logic in the models, and only use the controllers to give the right objects to your views, and taking them for saving, for example. As the saying goes, "fat model, skinny controller".

Resources