Currently working on nested forms with the following railscast http://railscasts.com/episodes/196-nested-model-form-revised
I have an Opportunity model,
class Opportunity < ActiveRecord::Base
has_many :headings
accepts_nested_attributes_for :headings
end
a Heading model
class Heading < ActiveRecord::Base
belongs_to :opportunity
has_many :subheadings
accepts_nested_attributes_for :subheadings
end
and a Subheading model
class Subheading < ActiveRecord::Base
belongs_to :heading
end
Right now I am just working on the view for the new action for the Opportunity model.
<h1>Add an opportunity</h1>
<%= form_for(@opportunity, :html => {:role => 'form'}) do |f| %>
<%= f.fields_for :headings do |builder| %>
<%= render 'heading_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add heading", f, :headings %>
<%= f.submit %>
<% end %>
And the _heading_fields.html.erb partial:
<fieldset>
<%= f.label :title, "Heading" %>
<%= f.text_field :title %>
<%= f.hidden_field :_destroy %>
<%= link_to "Remove section", '#', class: "remove_fields" %>
<%= link_to_add_fields "Add section", f, :subheadings %>
</fieldset>
And finally the link_to_add_fields method in the application helper:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
Now I am receiving the following error for the render call in the link_to_add_fields function:
syntax error, unexpected tIDENTIFIER, expecting keyword_end
It seems to have something to do with the underscore in the name of the partial being rendered in the link_to_add_fields method as it goes away when the underscore is not present.
Greatly appreciate any help at solving this headache!
Debug
As mentioned in the comments, you're basically going to have an issue with the closing of your loops - Rails is expecting an end
call, but you've set an identifier of sorts instead.
Having looked through your code, I cannot see where the issue might be, so I'll give you some details on how I'd debug it:
I would remove any elements which I would presume would be causing the issue, until I got it to work
For example, I would begin by replacing the def link_to_add_fields(name, f, association)
with a piece of demo content:
def link_to_add_fields(name, f, association)
"test"
end
If this outputs the correct string, it means the error is inside this method (which I'm guessing anyway). If this was the case, I would then try this:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = "test"
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields})
end
If this works, it means the problem is likely going to be with the f.fields_for
loop which you're using. Although this is just my guess, it's basically how I'd debug - sequentially remove things until it actually works
Fields
If you're looking to add fields dynamically to your form, you may wish to use the cocoon
gem. The Railscast you're using is firstly a little out of date, and only allows you to add single fields to your form
The "right" way to do it is to use an ajax method, which I detail here
There's a very good site I found which showed you how to do it, but the site has since gone offline. Anyway, I'll explain it for you here:
#config/routes.rb
resources :controller do
collection do
get :add_field
end
end
This gives you a route to send ajax requests to:
Controller
#app/views/controller/form.html.erb
<%= form_for @model do |f| %>
<%= render partial: "fields", locals: { builder: f } %>
<%= link_to "Add Field", controller_add_field_path, id: "add_field", remote: true %>
<%= f.submit "test" %>
<% end %>
#app/views/controllers/your_controller.rb
Class YourController < ActiveRecord::Base
def add_field
@model = Model.new
@model.association.build
render "add_field", layout: false
end
end
Views
Now you need to render the add_field
view, with the fields_for
partial
#app/views/controller/_form.html.erb
<%= form_for @model do |f| %>
<%= render partial: "fields", locals: { builder: f } %>
<% end %>
#app/views/controller/_fields.html.erb
<%= f.fields_for :association, child_index: Time.now.to_i do |a| %>
<%= a.text_feld :your_fields %>
<% end %>
JQuery
All you need to do then is create a way to fetch the new fields:
#app/assets/javascripts/application.js.coffee
$ ->
$(document).on "ajax:success", "#add_field", (data) ->
el_to_add = $(data).html()
$('#new_form').append(el_to_add)