No commit activity in last 3 years
No release in over 3 years
Easily add nested forms to your forms
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 0
>= 0

Runtime

>= 4.0
 Project Readme

CoolNestedForms

Gem Version

Add dynamically generated forms to your model's form for any of its associations. For example if you have a Job model that has_many Task then this gem will assist you in adding all the JavaScript necessary to allow your users to click an "Add Task" button on your edit view, the view will then append a new form to the DOM with all the field attributes formatted for rails (id, name, etc) plus will add any necessary hidden fields for existing records (task.id).

There is also JS events raised when an entry is added or removed so you can hook up your code for pre-form submission editing.

Any entries added or removed won't update the DB until the form is submitted.

Installation

Add this line to your application's Gemfile:

gem 'cool_nested_forms'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install cool_nested_forms

Then require the JavaScript in your app/assets/javascripts/application.js

//= require cool_nested_forms

Usage Example

For this example I will use Job and Task models

Preparing your models

Job Model

class Job < ApplicationRecord
  has_many :tasks, :dependent => :destroy
  # we need to accepts_nested_attributes_for Task
  # :name is required in this example - Use your own required field here or remove the reject_if call
  # :allow_destroy => true so we can delete entries when passing the _destroy field
  accepts_nested_attributes_for :tasks, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end

Task Model

class Task < ApplicationRecord
  belongs_to :job
end

Preparing the Job Controller

class JobsController < ApplicationController
  # include the CoolNestedFormsHelper
  helper CoolNestedFormsHelper
  # other code #

  def job_params
    # :id and :_destroy are required. Substitute name with your own fields
    params.require(:job).permit(:name,
      :tasks_attributes => [:id, :name, :_destroy])
  end
end

The partials used to generate the new and existing tasks

We need to provide cool_nested_forms two partials. One for new and one for existing records. They will be placed under the Job's view folder. Make sure the id field of the container is set like so: id="<%= id %>"

app/views/jobs/_task_new.html.rb

<!--  the id is generated from the helper methods using this template so always include it like so -->
<div id="<%= id %>">
  <!-- _destroy field: used for removing the record -->
  <%= builder.hidden_field :_destroy, :class => 'cnf-removable' %>

  <!-- data to be collected -->
  <%= builder.label :name %>
  <%= builder.text_field :name %>

  <!--  the remove button. make sure to pass the id as target or
   it will default to the parent DOM element when removing the item -->
  <%= remove_entry_button "Remove", Task, {:target => id} %>
</div>

app/views/jobs/_task_edit.html.rb

<!--  the id is generated from the helper methods using this template so always include it like so -->
<div id="<%= id %>">
  <!-- _destroy field: used for removing the record -->
  <%= builder.hidden_field :_destroy, :class => 'cnf-removable' %>
  <%= builder.hidden_field :id %> <!-- keeps the id inside this div  -->

  <!-- data to be edited -->
  <%= builder.label :name %>
  <%= builder.text_field :name %>
  <!-- Note: if you not wish for your users to edit this field after it is added
  you could just display the content. -->

  <!--  the remove button. make sure to pass the id as target or
   it will default to the parent DOM element when removing the item -->
  <%= remove_entry_button "Remove", Task, {:target => id} %>
</div>

Adding functionality to the Job form

app/views/jobs/_form.html.erb

  <%= form_with(model: job, local: true) do |form| %>

    <!-- your other job fields go here -->

    <!-- this generates a template for JavaScript. Pass the Task model class here. -->  
    <%= new_entry_template(form,Task) %>

    <!-- this generates a button that adds a task into   <div id="tasks">. id="tasks"
    is automatically generated by the next helper by convention. Pass the Task model class here. -->  
    <%= new_entry_button("Add Task", Task )%>

    <!-- generates a container with any existing tasks. provides a location for
    placing new tasks. The id of the container is by default  <div id="tasks">
    (the association plural form). Pass the Task model class here. -->
    <%= entries_container(form,Task,job.tasks) %>

  <%end%>

After add/remove events

If you need to perform any other javascript actions after an entry is added or removed, you can add a listener to these events

coolNestedForms.entryAdded
coolNestedForms.entryRemoved

Something like this

$(document).bind('coolNestedForms.childAdded', function(){
  // do something
});

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/CarlosRoque/cool_nested_forms. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.