For example,
You are creating a profile page where user can enter multiple locations. So for that create 2 models Profile and Locations.
In app/models/profile.rb
class Profile < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true
end
class Location < ActiveRecord::Base
belongs_to :profile
# In this model you can add validations like
validates_format_of :phone_number
end
Now, "accepts_nested_attributes_for" is very important. If you missed it then it will give you Error "nil.errors" when you will try to run the application.
In the profile controller you have to right
class ProfilesController < ApplicationController
def new
@profile.locations.build #(You can add the condition if you dont want to build the location object everytime)
end
def create
@profile = Profile.new(params[:profile])
@profile.save
end
end
In app/views/profiles/new.html.erb
<% form_for :profile, @profile, :url => profile_path(params[:id]), :method => :post do |f| %>
<%= f.text_field :name %>
<% f.fields_for :locations do |location| %>
<%= render "location_fields", :f => location %>
<% end %>
# if you want the user to add the locations dynamically then add this line
<%= link_to_add_fields "Add Location", f, :locations %>
<%= link_to_remove_fields "Remove Location", f %>< /p> <% end %>
In Application helper add this two methods to add and remove the fields(partials) dynamically
module ApplicationHelper
def link_to_remove_fields(name, f)
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
end
def link_to_add_fields(name, f, association)
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"))
end
end
Create a js file and put these methods in that file
function remove_fields(link) {
$(link).prev("input[type=hidden]").val("1");
$(link).closest(".fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).parent().before(content.replace(regexp, new_id));
Now when you Click on "Add location" then actually the helper method render the partial on the view page. When it render the page it creates a unique id it identify the locations fields.
So when you submit the form you will see the parameters as
params : { :profile => {:name => nil, :location_attributes => {"0" => {"address" => "ABC", "_delete" => ""}, "123456789" => {"address" => "XYZ", "_delete" => ""} }}}
This means user has added two locations in the profile.
Now when user click on "Remove Location" then through javascript we set the "_delete" field to "1". So the nested_attributes will remove the relevant record from the database.
The parameters will go as :
params : { :profile => {:name => nil, :location_attributes => {"0" => {"address" => "ABC", "_delete" => ""}, "123456789" => {"address" => "XYZ", "_delete" => "1"} }}}
Also if you inspect the "Add Location" button carefully you will see that the partial is already rendered in the view, but when you click on that button the partial is visible to the user.
If you want to have access to the random number which generated at runtime e.g "123456789" then in the partial you just have to right "new_locations" i.e. "new_#{table_name}"