It's my first time here, and first time I use nested_form gem. Everything seemed to be ok, but the data from my "parent" model doesn't save.
Here is my code
<%= nested_form_for #project do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :tasks %>
<p><%= f.link_to_add "Add a task", :tasks %></p>
<%= f.submit %>
<% end %>
so, when I "submit", just the tasks are saved ok, but not the project name.
Any clue for me? did I miss something??
You need to add the attribute. eg name to attr_accessible.
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
accepts_nested_attributes_for :tasks, :allow_destroy => true
attr_accessible :name,:tasks_attributes ## <-- you need this line
end
Your fields_for declaration isn't quite right
<%= f.fields_for :tasks %>
Should be
<%= f.fields_for :tasks do |task_builder| %>
you are also missing an end for that declaration and a render to render the partial that has the nested fields for the associated object.
So you should end up with something like this
<%= f.fields_for :tasks do |task_builder| %>
<%= render 'task_fields', :f => task_builder %>
<% end %>
<p><%= f.link_to_add "Add a task", :tasks %></p>
That should do the trick. all you need to do now is create a _task_field.html.erb partial and add the task fields to it in the usual way using f.label, f.text_field etc...
p.s.
Your code could not possibly have ever worked. You would have had errors so something is probably missing from your opening post.
Related
I have three models, and i am trying to save onto a third table using simple form gem and checkboxes.
class Work < ActiveRecord::Base
has_many :skills, through: :skillships
end
The second model is
class Skill < ActiveRecord::Base
has_many :works, through: :skillships
end
The third is
class Skillship < ActiveRecord::Base
belongs_to :work
belongs_to :skill
end
Using the Work model i am trying to save the data on the skillship table. Something similar to this http://railscasts.com/episodes/17-habtm-checkboxes-revised. Can you please help.
EDIT
The form
<%= simple_form_for(#work) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title, :label => 'Project Title' %>
<%= f.input :excerpt, :as => :text %>
<fieldset>
<legend>Skills Used </legend>
Would like to check the skills i used here.
</fieldset>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I tried..
<%= hidden_field_tag "work[skill_ids][]", nil %>
<% Skill.all.each do |skill| %>
<%= check_box_tag "work[skill_ids][]", skill.id, #work.skill_ids.include?(skill.id), id: dom_id(skill) %>
<%= label_tag dom_id(skill), skill.title %><br>
<% end %>
The reason i'm doing this it because work can have many skills used.
I was going about this the wrong way. A join table solved the problem.
rails g migration create_skills_works_table
Then
class CreateSkillsWorksTable < ActiveRecord::Migration
def self.up
create_table :skills_works, :id => false do |t|
t.references :skill
t.references :work
end
add_index :skills_works, [:skill_id, :work_id]
add_index :skills_works, [:work_id, :skill_id]
end
def self.down
drop_table :skills_works
end
end
Using simple form on the work view.
<fieldset>
<legend>Skills Used </legend>
<%= f.association :skills %>
</fieldset>
I have a Sezzion model:
attr_accessible :description
has_many :session_instructors, :dependent => :destroy
has_many :instructors, :through => :session_instructors
accepts_nested_attributes_for :session_instructors
accepts_nested_attributes_for :instructors
Instructor model:
attr_accessible :bio
has_many :sezzions, :through => :session_instructors
has_many :session_instructors, :dependent => :destroy
SessionInstructor model:
attr_accessible :instructor_id, :sezzion_id
belongs_to :sezzion
belongs_to :instructor
Lastly, User model:
has_many :sezzions
has_many :instructors
I'm trying to create a form for Sezzion with nested form for SessionInstructor which has multiple select option for Instructors.
How can I do the following:
nested form for SessionInstructor
use multiple select option to get all the selected Instructor's instructor_id
hidden field to pass in the created/updated session_id with each select instructor
I have the following code as of now:
<%= form_for(#sezzion) do |f| %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.label :instructors %>
<%= fields_for :session_instructors do |f| %>
<select multiple>
<% current_user.instructors.each do |instructor| %>
<option><%= instructor.name %></option>
<% end %>
</select>
<% end %>
<%= f.submit %>
<% end %>
Thank you so much!
This is something that seems ridiculously hard in Rails.
I think something like this might work:
<%= f.fields_for :session_instructors do |si| %>
<%= si.collection_select :instructor_ids, current_user.instructors, :id, :name, multiple: true>
<% end %>
This should create a form element which will set sezzion[session_instructors_attributes][instructor_ids].
Although I'm not sure if that's actually what you want. I've never tried this using a multi select. If it doesn't work, you could also try getting rid of the fields_for and just using f.collection_select. If you're willing to use a checkbox, I can show you how to do that for sure.
I hope that helps.
Edit:
Here's how I would usually do it with a check_box:
<%= f.fields_for :session_instructors do |si| %>
<%= si.hidden_field "instructor_ids[]" %>
<% current_user.instructors.each do |i| %>
<%= si.check_box "instructor_ids[]", i.id, i.sezzions.include?(#sezzion), id: "instructor_ids_#{i.id}" %>
<%= label_tag "instructor_ids_#{i.id}", i.name %>
<% end %>
<% end%>
There are a couple "gotchas!" with this method. When editing a model, if you deselect all checkboxes then it won't send the parameter at all. That's why the hidden_field is necessary. Also, you need to make sure each form element has a unique id field. Otherwise only the last entry is sent. That's why I manually set the value myself.
I copy pasted and then edited. Hopefully I got the syntax close enough where you can get it to work.
FINAL EDIT:
Per Sayanee's comment below, the answer was a bit simpler than I thought:
<%= f.collection_select :instructor_ids, current_user.instructors, :id, :name, {}, {:multiple => true} %>
#Sayanee, can you post how your instructors, sezzions table look like. Also for note, you can not get instructor_ids from Instructor object, hence you are getting "undefined method" error. With the current association that you shared, you can get instructor_ids from a Sezzion object. So you need to loop through current_user.sezzions in stead of current_user.instructors.
This is a way to implement fields_for nested form with html multiple_select in case of a has_many :through association. Solved it by doing something like this. The form view:
<%= form_for(#sezzion) do |f| %>
...
<%= fields_for :session_instructors do |g| %>
<%= g.label :instructor, "Instructees List (Ctrl+Click to select multiple)" %>:
<%= g.select(:instructor_id, Instructor.all.collect { |m| [m.name, m.id] }, {}, { :multiple => true, :size => 5 }) %>
<%= g.hidden_field :your_chosen_variable_id, value: your_chosen.id %>
<% end %>
...
<%= f.submit %>
<% end %>
Note:Since the #sezzion would not be saved at the time of generating the form you cannot pass that id (#sezzion.id) in place of your_chosen.id through the form. You could handle that save in the controller.
Make sure that your controller Initializes the Variables while generating the form: Your def new could look something like this:
def new
#sezzion = Sezzion.new
#sezzion.session_instructor.build
#sezzion.instructors.build
end
Now the create controller has to be able to accept the strong params required for the multiple select, so the sezzion_params method may look something like this:
def sezzion_params
params.require(:sezzion).permit(:description, :any_other_fields,
:session_instructors_attributes =>
[:instructor_id => [], :your_chosen_id => Integer])
end
In the create function, the first session_instructor variable is linked to the #sezzion instance variable through our new function. The other session_instructors in our multiple select must be built after the Sezzion instance is saved, if you want to pass in the created #sezzion.id with each select instructor. .
def create
#sezzion = Sezzion.new(sezzion_params)
#startcount=1 #The first element of the array passed back from the form with multiple select is blank
#sezzion.session_instructors.each do |m|
m.instructor_id = sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount]
m.your_chosen_variable_id = your_chosen.id
#startcount +=1
end
if #sezzion.save
sezzion_params["session_instructors_attributes"]["0"]["instructor_id"].drop(#startcount).each do |m|
#sezzion.session_instructors.build(:instructor_id => sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount],
:your_chosen_variable_id => your_chosen.id).save
#startcount += 1
end
flash[:success] = "Sezzion created!"
redirect_to root_url
else
flash[:danger] = "There were errors in your submission"
redirect_to new_sezzion_path
end
end
Given a User who can possibly be an Artist:
class User < ActiveRecord::Base
has_one :artist
end
I've got a User & Artist nested form (using Formtastic gem):
<h1>Artist registration</h1>
<% #user.build_artist unless #user.artist %>
<%= semantic_form_for #user, :url => create_artist_path do |f| %>
<%= f.inputs :username %>
<%= f.semantic_fields_for :artist do |a| %>
<%= a.input :bio %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button 'Register as Artist' %>
<% end %>
<% end %>
The problem is the :artist fields are not rendered.
I've also tried f.inputs :for => :artist do |a|.
For some reason, using #user.build_artist does not display the artist's fields in the form. If I try #user.artist = Artist.new I get an error, because it tries to save the Artist and validation fails.
How should I initialize the Artist model so I get the benefit of formtastic generators in a nested form? (Note that #user here is not a :new_record?)
Did you remember to set accepts_nested_attributes_for :artist in user.rb?
In my first rails app I'm trying to use form_for and fields_for to create a nested object form. So far so good, but I can't figure out how to access the sub-object while in the fields_for block. I've pre-populated a field in the sub-object with data that I want to show in the user instructions.
Models
Garage:
has_many :cars, :dependent => :destroy
accepts_nested_attributes_for :cars
Car:
belongs_to :garage
Garage Controller
def new
#garage = Garage.new
for i in 1..5
#garage.cars.build :stall_number => i
end
end
_form.html.erb
<%= form_for #garage do |f| %>
<%= f.label :title, "Garage Name" %><br />
<%= f.text_field :title %>
<% f.fields_for :cars do |builder| %>
<p>Enter license for car parked in stall: <%= car.stall_number %></p>
<%= f.label :license, "License #:" %><br />
<%= f.text_field :license %>
<%= end %>
<%= end %>
As you can see, inside the builder block for :cars, I want to show, in my user instructions, the field: car.stall_number (populated in my controller with an integer):
<p>Enter license for car parked in stall: <%= car.stall_number%></p>
I've tried a many different ideas: #car.stall_number, object.car.stall_number, etc. No joy. Multiple searches and a look at the fields_for source code haven't helped my understanding. I would appreciate any guidance.
Update: For clarification, per Dan's suggestion I have tried builder.stall_number but it results in a
NoMethodError: undefined method 'stall_number' for #<ActionView::Helpers::FormBuilder:0x00000102a1baf0>
I just dealt with this today myself.
You can access the object of the fields_for through:
builder.object
where builder is your fields_for form builder object. In your particular case, you can say:
<p>Enter license for car parked in stall: <%= builder.object.stall_number%></p>
That should do it for you!
The way you are trying is does not work because you want to access car without filling that variable for data.
I guess you want to have multiple blocks of stalls, where you can enter license plates. For each stall you will need your own fields_for.
I would suggest something like that:
<%= form_for #garage do |f| %>
<%= f.label :title, "Garage Name" %><br />
<%= f.text_field :title %>
<% for i in 1..5 %>
<% f.fields_for #garage.cars[i] do |builder| %>
<p>Enter license for car parked in stall: <%= builder.stall_number%></p>
<%= builder.label :license, "License #:" %><br />
<%= builder.text_field :license %>
<% end %>
<% end %>
<% end %>
Within the fields_for you need to use the form object you define there, in this case builder. Since the data there are not mapped to the outer form (f), but to the cars object (builder).
I'm trying to build a simple nested form, checking a lot of resources online, but can't find what is it that I'm missing!
I have the following:
class Configuration < ActiveRecord::Base
has_many :configoptions
accepts_nested_attributes_for :configoptions
end
class Configoption < ActiveRecord::Base
belongs_to :configuration
has_many :items
end
Now, I'm trying to make a simple form when you select a configuration so it would show the
configoptions belonging to it, but nothing works!
This is the view without any html
<%= form_for :config do |f| %>
<%= f.text_field(:name)%>
<% f.fields_for #options do |option|%>
<% end %>
<% end %>
In the controller I have:
def show
#config = Configuration.find(params[:id])
#options = #config.configoptions
end
But i end up getting the error:
undefined method `model_name' for Array:Class
Does anyone have advice for me? Thanks a lot!
FYI, Ryan Bates (RailsCasts) has created a gem to handle much of this. I'm using it now and it works great!
See https://github.com/reu/simple_nested_form for the details.
You'll have to specify a model_name if you are using a collection (like an array).
<%= form_for :config do |f| %>
<%= f.text_field(:name)%>
<%= f.fields_for :configoptions, #options do |option|%>
<%= option.text_field(:some_attribute) %>
<% end %>
<% end %>
In fields_for I am passing two arguments, :configoptions as the model name and #options as the collection to use. http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html has a ton of great nested attribute examples if you scroll down just above half way.
Alternatively (and my personal preferred method) is to loop through the collection and call fields_for for each object.
<%= form_for :config do |f| %>
<%= f.text_field(:name)%>
<% #options.each do |configoption| %>
<%= f.fields_for configoption do |option|%>
<%= option.text_field(:some_attribute) %>
<% end %>
<% end %>
<% end %>
Try:
class Configuration < ActiveRecord::Base
has_many :configoptions, :dependent => :destroy
accepts_nested_attributes_for :configoptions, :allow_destroy => true
end
You used:
<% f.fields_for #options do |option|%>
try using
<%= f.fields_for #options do |option|%>
I had the same problem, it was solved by this trivial idea.