I am having a bit of difficulty getting my "update form" to display the nested attributes. Specifically, images (e.g., "choices") to display. All other data fields are showing. Just not this is my form:
<%= bootstrap_form_for #template, :url => {:action => 'update', :id => #template.id } do |f| %>
<fieldset>
<legend>Update Your Template</legend>
<%= f.text_field :prompt, :class => :span6, :placeholder => "Which one is running?", :autocomplete => :off %>
<%= f.select 'group_id', options_from_collection_for_select(#groups, 'id', 'name', selected: #template.group.id) %>
<div class="row-fluid">
<ul class="thumbnails">
<%= f.fields_for :template_assignments do |builder| %>
<li class="span3" id="">
<div class="thumbnail">
<%= builder.text_field :choice_id %>
<%= image_tag #template.template_assignments.builder.choice.image %>
</div>
</li>
<% end %>
</ul>
</div>
<% end %>
The main line I am having trouble with is:
<%= image_tag #template.template_assignments.builder.choice.image %>
I cannot get it to iterate through each of the 4 nested attributes for the image. It iterates through the 4 nested attributes pertaining to :choice_id, which displays correctly in the text_field.
If i change it to:
<%= image_tag #template.template_assignments.first.choice.image %>, it displays the first image no problem.
However, I need it to iterate and display the "first", "second", "third", and "fourth" images.
Any help on how to display these images, just as the image_id's are being displayed?
EDIT:
Here are my models
# app/models/template.rb
class Template < ActiveRecord::Base
belongs_to :group
has_many :template_assignments, dependent: :destroy
has_many :choices, :through => :template_assignments
accepts_nested_attributes_for :template_assignments, allow_destroy: true
end
# app/models/template_assignment.rb
class TemplateAssignment < ActiveRecord::Base
belongs_to :template
belongs_to :choice
end
# app/models/choice.rb
class Choice < ActiveRecord::Base
has_many :template_assignments
has_many :templates, :through => :template_assignments
end
You'll probably want to just use builder directly, just like you're doing in the text_field.
<%= image_tag builder.choice.image %>
[UPDATE] after some trial and error the correct form would be :
<%= image_tag builder.object.choice.image %>
What's happening is that when f.fields_for :template_assignments do |builder| is used to render the nested items, the builder object that is yielded to the block is not the object itself (in this case a TemplateAssignment), but is a FormBuilder object, which is what supplies the convenience methods like builder.text_field. (If you tried to do template_assignment.text_field you'd get an error.) The builder stores the object that it is representing in the form as object, so you can get a hold of your template_assignment object by using builder.object. From there you can deal the the template_assignment like normal. I hope that helps.
Related
I have three tables - result, feedback_qs, and feedback_qs_results (join table). Given an existing result and feedback_qs, I want to populate the join table with the result_id, feedback_q_id and the subsequent answer (text_area).
I believe the relationships are correct....
# result.rb
has_many :feedback_q_results
has_many :feedback_qs, :through => :feedback_q_results
# feedback_q.rb
has_many :feedback_q_results
has_many :results, :through => :feedback_q_results
# feedback_q_result.rb
belongs_to :result
belongs_to :feedback_q
I have access to the result_id (result/result.id/feedback) via config/routes.rb below...
resources :results do
member do
get 'feedback'
post 'feedback_create'
end
end
What would this form look like to populate the join table? Something similar to this (I realize this isn't correct)...? How do I take in the data through the post_controller to populate the join table? Or should it go through another controller?
<%= form_for :result, url: feedback_create_result_path(result), method: :post do |form| %>
<% for q in FeedbackQ.all %>
<div class="col field">
<p>
<%= form.label q.question_text %>
</p>
<%= text_area_tag id: q.id %>
</div>
<% end %>
<div class="actions">
<%= form.submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
What would my feedback_create method look like? While I've seen several questions regarding rails' many to many relationships and populating join tables, none of those use cases seems to suit my need. Thanks in advance.
Update - My use case is this: user takes an exam (called a result), and they then review the proctor by answering feedback questions. I want to record the feedback in the join table
If your has_many associations are working correctly, then in the rails console you should be able to do:
> result.feedback_qs
=> []
and
> feedback_q.results
=> []
Where result and feedback_q are are Result and FeedbackQ objects, respectively.
To associate a feedback_q to a result, simply:
> result.feedback_qs << feedback_q
This should create a record in the feedback_q_result table.
Now, when you do
> result.feedback_qs
It should return the feedback_q record that you just added.
I altered my original models. One issue I ran into was simple naming convention in rails, so I simplified the table names to eliminate that from the equation. New models:
# result.rb
has_many :feedbacks
has_many :questions, :through => :feedbacks
# question.rb
has_many :feedbacks
has_many :results, :through => :feedbacks
# feedback.rb
belongs_to :question
belongs_to :result
validates_uniqueness_of :question_id, scope: :result_id
form in my view:
<%= form_for :result, url: feedback_create_result_path(result), method: :post do |form| %>
<% Question.all.each do |question| %>
<div class="col field">
<p>
<%= form.label :question, question.question_text %>
</p>
<%= hidden_field_tag 'question[][id]', question.id %>
<%= text_area_tag 'question[][answer]', "", id: "question_" + question.id.to_s, class: "stretch_textarea" %>
</div>
<% end %>
<div class="actions">
<%= form.submit "Submit", class: "btn btn-primary" %>
</div>
This SO page helped quite a bit as well.
I have the following models:
class RandomExam < ActiveRecord::Base
has_many :random_exam_sections
has_many :sections, :through => :random_exam_sections
end
class Section < ActiveRecord::Base
has_many :random_exam_sections
has_many :random_exams, :through => :random_exam_sections
class RandomExamSection < ActiveRecord::Base
belongs_to :random_exam
belongs_to :section
end
The idea is to have certain configurations to create random exams, so this tables help to select which sections do you need and then also select the number of questions per section, here are the attributes of each table:
RandomExam: name(string), created_at(datetime), updated_at(datetime)
Section: name(string), created_at(datetime), updated_at(datetime)
RandomExamSection: random_exam_id(integer), section_id(integer), questions_number(integer)
As you can see the number of questions per section attribute is inside the RandomExamSections table and I want to access it in a form that is displayed from the RandomExam controller, here is my form:
<%= form_for (#random_exam) do |f| %>
<div class="row">
<div class="input-field col s12">
<%= f.label :name, 'Name' %>
<%= f.text_field :name, placeholder: 'Enter the name of the configuration' %>
</div>
</div>
<% #sections.each do |section| %>
<div class="row <%= dom_id(section) %>">
<div class="col s4">
<%= check_box_tag 'random_exam[section_ids][]', section.id,
#random_exam.section_ids.include?(section.id), id: dom_id(section), class: "section-checkbox #{dom_id(section)}" %>
<%= label_tag dom_id(section), (raw sanitize section.name, tags: %w(h2 p strong em a br b i small u ul ol li blockquote), attributes: %w(id class href)),
class: "name #{dom_id(section)}" %>
</div class="col s4">
<div>
<%= text_field_tag "random_exam[random_questions_numbers][#{section.id}][]", nil,
:placeholder => 'Enter the number of questions' %>
</div>
</div>
<% end %>
<div class="form-group">
<%= f.submit class: "btn waves-effect waves-light green" %>
</div>
<% end %>
My controller:
def create
#random_exam = RandomExam.new(random_exam_params)
if #random_exam.save
assign_random_questions_number
flash[:success] = 'Random configuration created successfully'
redirect_to #random_exam
else
flash.now[:danger] = #random_exam.errors.full_messages.to_sentence
render 'new'
end
def assign_random_questions_number
if params[:random_exam][:'random_questions_numbers'] == nil
return
end
params[:random_exam][:'section_ids'].each do |key, value|
record = RandomExamSection.search_ids(#random_exam.id, key)
record.each do |random_exam_section_record|
number_of_questions = params[:random_exam][:'random_questions_numbers'][key].first.to_i
random_exam_section_record.update(questions_number: number_of_questions)
end
end
end
I'm getting a TypeError: TypeError: nil is not a symbol nor a string when I update the record in the method assign_random_questions_number
This error even appears when I run this on the console
random = RandomExamSection.first
random.update(questions_number: 10)
Or when I run:
random = RandomExamSection.first
random.questions_number = 10
random.save
EDIT
I ended up deleting the association in RandomExamSection and recreating it inside 'assign_random_questions_number' with the questions_number
Thanks.
If you use nested_attributes you can do something like this:
#form
<h4>Selected exams</h4>
<%= f.fields_for :random_exam_sections do |b| %>
<%= b.hidden_field :section_id %>
<%= b.label :selected, b.object.section.name %>
<%= b.check_box :selected, { checked: !b.object.id.blank? } %>
<br>
<%= b.label :question_numbers %>
<%= b.text_field :questions_number %>
<% end %>
#RandomExamModel
class RandomExam < ApplicationRecord
has_many :random_exam_sections, inverse_of: :random_exam
has_many :sections, :through => :random_exam_sections
accepts_nested_attributes_for :random_exam_sections, reject_if: :is_not_selected
private
def is_not_selected(attr)
attr["selected"] == '0'
end
end
# RandomExam
class RandomExamSection < ApplicationRecord
belongs_to :random_exam
belongs_to :section
attr_accessor :selected
end
# Controller
# GET /random_exams/new
def new
#random_exam = RandomExam.new
#random_exam.random_exam_sections.build(Section.all.map{|s| {section_id: s.id}})
end
The idea basically is
- Build on controller the random_exam_sections to be selected
- Write a form that allows to you 'select' one option and assign the number
- Then, validate if the random_exam_section of a sections was selected (this why i made that `attr_accessor :selected`, i need a place to write if user select the exam_section)
- If was selected, save.
The trick here is build on the controller, then select on the view and validate the selected on the model. Here i made an example if you need help: https://github.com/afromankenobi/nested_attr_demo
To add sections when the random_exam_sections is already created you should probably use javascript. Maybe this railscasts can help you http://railscasts.com/episodes/196-nested-model-form-part-1
Edit: Essentially looking to pass something like this:
{
'tabled_id' : '1',
'recipes' : [{
{ 'recipe_id' : '3',
'quantity' : '2'
}
{ 'recipe_id' : '5',
'quantity' : '1'
}
}]
}
And I think I should do params.require(:order).permit(:table_id, {recipes:, [:id,:quantity]} ) on the controller side.
I'm learning Rails building an ordering system and I'm stuck trying to build a form for Orders that passes quantity. Where Orders is a nested resource for Restaurant.
My models look like this:
class Restaurant < ActiveRecord::Base
has_many :orders
has_many :recipes, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes, dependent: :destroy
has_many :recipes, through: :order_recipes
end
class Recipe < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes
has_many :orders, through: :order_recipes
end
View:
<%= form_for([#restaurant, #order]) do |order_form| %>
<%= order_form.label :Table_Number %>
<%= order_form.number_field :table_id %>
<h3>Recipes: </h3>
<br>
<% #restaurant.recipes.each do |recipe| %>
<%= order_form.fields_for :recipe, recipe do |r| %>
<%= r.label recipe.name %>
<%= r.hidden_field :id %>
<%= r.number_field :quantity %>
<% end %>
<% end %>
<%= order_form.submit(#order.new_record? ? "Create Order" : "Edit Order", class: "btn btn-success") %>
<% end %>
This will yield a form that looks correct, but won't pass all parameters. Let's say I have 3 recipes. And I set their quantities to 2,3,4 respectively, and the table_id to 1. When I inspect the parameters, I see that only the last recipe with its quantity has been passed. params[:order] => {"table_id"=>"1", "recipe"=>{"id"=>"4", "quantity"=>"4"}} I need to be able to send all recipes with their assigned quantities. Also, I'm using the accepted answer in this question to be able to access the quantity column: Rails 4 Accessing Join Table Attributes
When you hand in fields_for :recipes multiple times, the fields_for method is not aware of you sending an array of things. Therefore it will name the parameters as if it was only one instance, so only the last instance will come through. You have to hand in the array of recipes to the fields_for, so it can name the parameters, so that rails knows it is an array of things when it gets picked up again (docs).
This is because form parameters in browsers do not support nesting by default. The actual parameters are flat key-value paramters. Rails has some naming conventions on how paramters can be named, so they will automatically be coerced to an array.
<%= form_for([#restaurant, #order]) do |order_form| %>
<%= order_form.label :Table_Number %>
<%= order_form.number_field :table_id %>
<h3>Recipes: </h3>
<br>
<%= order_form.fields_for :recipes, #restaurant.recipes do |r| %>
<%= r.label recipe.name %>
<%= r.hidden_field :id %>
<%= r.number_field :quantity %>
<% end %>
<%= order_form.submit(#order.new_record? ? "Create Order" : "Edit Order", class: "btn btn-success") %>
<% end %>
I'm working in Rails 4/Ruby 2.0.0. I have a two models - Articles and Graphics. Articles has_many Graphics. So, in my code I am trying to add an empty record to the graphics collection on the article so that in the form, there will be an empty set of fields to let a new record be added. I cannot figure out why the fields do not show up on the form though.
I've tried multiple methods of building the graphics collection but none seem to do the trick. Surely I must be missing something insanely small.
Article.rb
class Article < ActiveRecord::Base
has_many :graphics, :dependent => :destroy, :foreign_key => 'article_id'
accepts_nested_attributes_for :graphics,
:allow_destroy => true,
:reject_if => :all_blank
end
Graphic.rb
class Graphic < ActiveRecord::Base
belongs_to :article
validates_presence_of :path, :caption
end
_form.html.erb
...
<% f.fields_for :graphics do |g| %>
<div class="clear clearfix pad-b-20">
<div class="w-1-2 left f-left">
<div class="field">
<%= g.label :path %><br>
<%= g.text_field :path %>
</div>
</div>
<div class="w-1-2 left f-left">
<div class="field">
<%= g.label :caption %><br>
<%= g.text_field :caption %>
</div>
</div>
</div>
<% end %>
...
Building it in a form helper method
articles/_form.html.erb
<%= form_for(setup_article(#article)) do |f| %>
form_helper.rb
module FormHelper
def setup_article(article)
article.graphics.build
article
end
end
Using an ActiveRecord callback
Article.rb
...
after_initialize :build_graphics
private
def build_graphics
self.graphics.build
end
Building it in the controller
ArticleController.rb
...
def new
#article = Article.new
#article.graphics.build
end
...
The problem is that both for form_for and for fields_for you need to use <%=, because they render the contents of the form.
So, to solve your problem, you need to write
...
<%= f.fields_for :graphics do |g| %>
Your content
<% end %>
...
I'm trying to implement a has_many :through join in Rails 3 (with Formtastic) and I'm a little stuck. I have the models set up as follows:
Models:
class Project < ActiveRecord::Base
has_many :employees, :through => :teams
has_many :teams
class Employee < ActiveRecord::Base
has_many :projects, :through => :teams
has_many :teams
class Team < ActiveRecord::Base
belongs_to :project
belongs_to :employee
And this line gives me a multi-select box in the projects view that allows employees to be selected:
View:
<%= f.input :employees, :as => :select %>
So far this gets the job done, but what I'd LIKE to have is a separate dropdown box to select each employee's name, then their role in the project. I can't figure out the form code to get me there...
EDIT:
As suggested, I've gotten the code from Railscast 197: Nested Model Forms working and it's part-way there. This is what I have in the view:
<%= f.semantic_fields_for :employees do |builder| %>
<%= render 'employee_fields', :f => builder %>
<% end %>
<%= link_to_add_fields "add employee", f, :employees %>
and the 'employee_fields' partial:
<p class="fields">
<%= f.input :name, :as => :select, :collection => Employee.find(:all) %>
<%= f.hidden_field :_destroy %>
<%= link_to_remove_fields "remove", f %>
</p>
But right now this creates a new employee rather than a new team (project-employee join record), so I think it's acting as a has_many rather than a has_many :through. How can I edit this so that the :name input above adds a record to project[employee_ids][]?
Oh my god, I finally got this thing to work. Here's the relevant code, minus the bits added to make the form add and remove fields dynamically:
_form.html.erb
<%= semantic_form_for #project do |f| %>
.
.
<%= f.semantic_fields_for :teams do |builder| %>
<input id="project_teams_none" name="team[employee_ids][]" type="hidden" value="" />
<%= render 'team_fields', :f => builder %>
<% end %>
_team_fields.html.erb
<div class="input">
<%= f.collection_select(:employee_id, Employee.all, :id, :name, :include_blank => true ) %>
</div>
The key was adding the <input id="project_teams_none" name="team[employee_ids][]" type="hidden" value="" /> line in manually, because for whatever reason this wasn't generated as part of the form. This got the form to actually start updating things, and then I just had to make the nested form refer to the join model (team) rather than to employees so that the updates were going to the right place.
Looks so simple now!