In this blog, I'll show how to create complex forms in Rails 4, 
                where one can "add fields on the fly" by using JavaScript. 
                This issue on how to "add fields on the fly" inspired me 
                when I was learning how to create complex forms by using Ruby on Rails. 
                While learning, I went through the Rails Guide which covers 
                 
               complex forms
                creation in detailed way, apart from  
                showing how to 
               "add fields on the fly". 
                "Adding fields on the fly" is not covered fully, 
                as it's not directly supported by Rails, because
                 it involves the usage of JavaScript. Well, I was 
                intrigued by this issue, and I wanted to know how 
                to mix JavaScript with complex forms creation.
       
        Following Peter Rhoades' 
        excellent article's steps, I decided to approach 
        this issue from the very start, step-by-step: First, I'll create the rails project,
        and then create the Person and Address models used in this sample. 
        After that I'll update _form partial 
        that is used for Person and Address creation.
        Finally, some JavaScript is added to the project to allow "adding fields on the fly", which 
        also makes a more interactive look to the page. 
       
About the models, the Person model can have many Addresses, and the Address model belongs to one Person model. By the way, those models are identical to the ones that are presented in the Rails Guide; only the JavaScript part is new. If you want to see JavaScript right away, you can advance directly to the second section of this blog. The source code for this blog is available at GitHub.
Preparation
         
          Okay, lets start by creating a new rails project on command line: 
            rails new nested_forms
 
 
            Then install the gems in the project bundle: 
             cd nested_forms 
             bundle install 
 
            Then generate scaffold for Person, and generate model for Address: 
             rails generate scaffold Person name 
             rails generate model Address kind street person_id:integer 
 
            Finally migrate those new models into database
            bundle exec rake db:migrate 
       
            Then update your Person model by adding association: 
            has_many :addresses. 
            Add also line accepts_nested_attributes_for :addresses
            to allow Address creation in the same controller where the Person is created.
            
             Ruby on Rails API says: 
            "Nested attributes allow you to save attributes on associated records through the parent."
            And like mentioned in the Rails Guide: 
            
           "This creates an addresses_attributes= 
                method on Person that allows you to create, 
           update and (optionally) destroy addresses."
      
           # file: app/models/person.rb 
           class Person < ActiveRecord::Base
           has_many  :addresses
           accepts_nested_attributes_for  :addresses
           end
             
      
        Next add belongs_to :person association to 
        the generated Address model:
      
           # file: app/models/address.rb 
           class Address < ActiveRecord::Base
           belongs_to  :person
           end
           
      Then update People controller's strong parameters in method person_params.
        This allows Address creation at the same time when Person is created.
        Also update the new action to so 
          that it builds two Addresses for new Person.
        # file: app/controllers/people.rb 
        class PeopleController < ApplicationController 
        def new
        @person = Person.new  
        2.times { @person.addresses.build }
        end 
        ....some code left out.... 
        def person_params
        params.require(:person).permit(:name, 
        :addresses_attributes => [:id, :kind, :street])
        end
        end
    
            class PeopleController < ApplicationController
              before_action :set_person, only: [:show, :edit, :update, :destroy]
              # GET /people
              # GET /people.json
              def index
                @people = Person.all
              end
              # GET /people/1
              # GET /people/1.json
              def show
                @addresses = @person.addresses
              end
              # GET /people/new
              def new
                @person = Person.new
                2.times { @person.addresses.build}
              end
              # GET /people/1/edit
              def edit
              end
              # POST /people
              # POST /people.json
              def create
                @person = Person.new(person_params)
                respond_to do |format|
                  if @person.save
                    format.html { redirect_to @person, notice: 'Person was successfully created.' }
                    format.json { render action: 'show', status: :created, location: @person }
                  else
                    format.html { render action: 'new' }
                    format.json { render json: @person.errors, status: :unprocessable_entity }
                  end
                end
              end
              # PATCH/PUT /people/1
              # PATCH/PUT /people/1.json
              def update
                respond_to do |format|
                  if @person.update(person_params)
                    format.html { redirect_to @person, notice: 'Person was successfully updated.' }
                    format.json { head :no_content }
                  else
                    format.html { render action: 'edit' }
                    format.json { render json: @person.errors, status: :unprocessable_entity }
                  end
                end
              end
              # DELETE /people/1
              # DELETE /people/1.json
              def destroy
                @person.destroy
                respond_to do |format|
                  format.html { redirect_to people_url }
                  format.json { head :no_content }
                end
              end
              private
                # Use callbacks to share common setup or constraints between actions.
                def set_person
                  @person = Person.find(params[:id])
                end
                # Never trust parameters from the scary internet, only allow the white list through.
                def person_params
                  params.require(:person).permit(:name, :addresses_attributes => [:id, :kind, :street])
                end
            end
         
         Finally add fields_for, label 
            and text_field 
          form helpers and some text for adding Address to the _form partial:
        # file: app/views/people/_form.html.erb 
        Addresses:
        <ul> 
            <%= f.fields_for :addresses do |addresses_form| %> 
                <li>
                <%= addresses_form.label :kind %>
                <%= addresses_form.text_field :kind %>
                <%= addresses_form.label :street %>
                <%= addresses_form.text_field :street %>
                </li>
            <% end %>
        </ul>
        <div class="actions">
            <%= f.submit "Create Person and Addresses" %>
        </div>       
        
      
<%= form_for(@person) do |f| %>
  <% if @person.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@person.errors.count, "error") %> 
          prohibited this person from being saved: </h2>
      <ul>
          <% @person.errors.full_messages.each do |msg| %>
            <li><%= msg %> </li>
          <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  Addresses:
  <ul>
    <%= f.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
 
        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
      </li>
    <% end %>
  </ul>
  <div class="actions">
    <%= f.submit "Create Person and Addresses" %>
  </div>
<% end %>
      
      Now if you want to create a new Person, then you can also create two Addresses at the same time:
If you look at the generated source code below and the created Address elements, you can see that the hash key that is used for identifying an Address is an integer, and starts from 0; the second Address is identified with a hash key of 1. I have used Chrome's Developer Tools for viewing those elements below. No user entered data is shown in this view, because the form hasn't been submitted yet.
Now if you submit the data, you have created two Addresses for Person, who in this case is Bart. Whenever you want to create a Person, then those two Address fields are shown each time. Therefore, the people/new page is a bit static at the moment, but let's change that by allowing "Adding Fields on the Fly" functionality.
"Adding Fields on the Fly"
      As shown previously the key value for the Address hash is created incrementally 
      by using integer numbers such as 0 and 1. This means that the addresses that are created 
      are unique. In our sample we aren't going to increment integers for hash keys 
      (although I think it could work);  
      instead let's follow the Rails Guide:
       "When generating new sets of fields you must ensure the key of the associated array is unique 
      - the current JavaScript date (milliseconds after the epoch) is a common choice." 
      So let's use JavaScript 
          Date object in place of added integer values.
        
          Now we are going to make JavaScript code that creates the Address
          elements shown before and uses JavaScript Date
           object in place of 0 and 1 values for
          keys. Also those elements can only be displayed and created on
          the form after clicking an "Add address" button like shown below:
      
After two "Add address" button clicks and some user entered information we get the following view:
 To get the functionality that was shown in the two screenshots above,  
           and to make JavaScript changes that were mentioned earlier,
           we need to add some JavaScript to our project. 
           Let's add a JavaScript function called addAddressField
           to app/assets/javascripts folder in a file called people.js.
           Main parts of the function code is highlighted below. You
           can also view the full code by clicking the link View Full Code.
       
    # file: app/assets/javascripts/people.js 
    function addAddressField() {
    //create Date object 
    var date = new Date();
    //get number of milliseconds since midnight Jan 1, 1970  
    //and use it for address key 
    var mSec = date.getTime();
    //Replace 0 with milliseconds 
    idAttributKind = 
           "person_addresses_attributes_0_kind".replace("0", mSec);
    nameAttributKind = 
           "person[addresses_attributes][0][kind]".replace("0", mSec);
           
    //create <li> tag  
    var li = document.createElement("li");           
    ....some code left out.... 
               
    //create input for Kind, set it's type, id and name attribute, 
    //and append it to <li> element  
    var inputKind = document.createElement("INPUT");
    inputKind.setAttribute("type", "text");
    inputKind.setAttribute("id", idAttributKind);
    inputKind.setAttribute("name", nameAttributKind);
    li.appendChild(inputKind);
    ....some code left out....
    //add created <li> element with its child elements 
    //(label and input) to myList (<ul>) element
    document.getElementById("myList").appendChild(li);
    //show address header
    $("#addressHeader").show();
    }
View Full Code
function addAddressField() {
    //create Date object
    var date = new Date();
    //get number of milliseconds since midnight Jan 1, 1970 
    //and use it for address key
    var mSec = date.getTime(); 
    //Replace 0 with milliseconds
    idAttributKind =  
          "person_addresses_attributes_0_kind".replace("0", mSec);
    nameAttributKind =  
          "person[addresses_attributes][0][kind]".replace("0", mSec);
    idAttributStreet =  
          "person_addresses_attributes_0_street".replace("0", mSec);
    nameAttributStreet =  
          "person[addresses_attributes][0][street]".replace("0", mSec);
       
    //create <li> tag
    var li = document.createElement("li");
    //create label for Kind, set it's for attribute, 
    //and append it to <li> element
    var labelKind = document.createElement("label");
    labelKind.setAttribute("for", idAttributKind);
    var kindLabelText = document.createTextNode("Kind");
    labelKind.appendChild(kindLabelText);
    li.appendChild(labelKind);
    //create input for Kind, set it's type, id and name attribute, 
    //and append it to <li> element
    var inputKind = document.createElement("INPUT");
    inputKind.setAttribute("type", "text");
    inputKind.setAttribute("id", idAttributKind);
    inputKind.setAttribute("name", nameAttributKind);
    li.appendChild(inputKind);
    //create label for Street, set it's for attribute, 
    //and append it to <li> element
    var labelStreet = document.createElement("label");
    labelStreet.setAttribute("for", idAttributStreet);
    var streetLabelText = document.createTextNode("Street");
    labelStreet.appendChild(streetLabelText);
    li.appendChild(labelStreet);
    //create input for Street, set it's type, id and name attribute, 
    //and append it to <li> element
    var inputStreet = document.createElement("INPUT");
    inputStreet.setAttribute("type", "text");
    inputStreet.setAttribute("id", idAttributStreet);
    inputStreet.setAttribute("name", nameAttributStreet);
    li.appendChild(inputStreet);
    //add created <li> element with its child elements 
    //(label and input) to myList (<ul>) element
    document.getElementById("myList").appendChild(li);
    //show address header
    $("#addressHeader").show(); 
}
       The above JavaScript creates new <li> element, two labels, and two input elements for Address entries, which are then appended on to <ul> element of the form. Also an address header is made visible by using jQuery.
          For id attribute values you don't have
          to use that long: 
            person_addresses_attributes_sth_kind  format;
          it can be something more shorter, for instance a plain integer. However,
          the format of the name
          attribute value has to look like to original one: 
           person[addresses_attributes][sth][kind]; otherwise the data won't be
          saved in the controller. Like said in the 
            
            Ruby Rails Tutorial book,
          by Michael Hartl: "These name values allow Rails 
           to construct an initialization hash (via the params variable) 
           for creating users using the values entered by the user." 
            In our case Person and Addresses are being created based on the values
           entered by the user.
      
          
          Finally, let's make some changes 
          to the _form partial, 
           so that it uses addAddressField function.
          Now we can remove the fields_for helper and the code 
           within fields_for helper method,
          and add submit_tag and Address header to get the functionality we need.
          The changes are small and are highlighted below.
          
       
    # file: app/views/people/_form.html.erb 
    <% submit_tag "Add address", :type =>  "button", :id => "addAddress",               :onclick => 'addAddressField()'%> 
    <br />
    <div id="addressHeader" style="display:none"> 
       Address information: 
    </div>
    <br />
    <ul id="myList">
    </ul>
    <div class="actions">
        <%= f.submit "Create Person and Addresses" %>
    </div>       
    
    
 <%= form_for(@person) do |f| %>
  <% if @person.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@person.errors.count, "error") %> 
          prohibited this person from being saved: </h2>
      <ul>
          <% @person.errors.full_messages.each do |msg| %>
            <li><%= msg %> </li>
          <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <%= submit_tag "Add address", :type => 'button', :id => 'addAddress', 
          :onclick => 'addAddressField()' %> 
  <br>
  <div id="addressHeader" style="display:none">Address information:  </div>
  <br>
  <ul id="myList">
  </ul>
  <div class="actions">
    <%= f.submit "Create Person and Addresses" %>
  <div>
<% end %>
      
    Now if you look at the generated source code again and the created Address elements, you can
           see that the hash key that is used for identifying an Address 
           are the milliseconds from the JavaScript Date object.
       
Finally we can create People and their Addresses by "Adding Fields on the Fly".





