• This repository has been archived on 12/Dec/2021
  • Stars
    star
    1,793
  • Rank 24,864 (Top 0.6 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 14 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rails plugin to conveniently handle multiple models in a single form.

Unmaintained

The Nested Form gem is no longer maintained. Feel free to fork this project.

Nested Form

Build Status

This is a Rails gem for conveniently manage multiple nested models in a single form. It does so in an unobtrusive way through jQuery or Prototype.

This gem only works with Rails 3. See the rails2 branch for a plugin to work in Rails 2.

An example project showing how this works is available in the complex-nested-forms/nested_form branch.

Setup

Add it to your Gemfile then run bundle to install it.

gem "nested_form"

And then add it to the Asset Pipeline in the application.js file:

//= require jquery_nested_form

Non Asset Pipeline Setup

If you do not use the asset pipeline, run this generator to create the JavaScript file.

rails g nested_form:install

You can then include the generated JavaScript in your layout.

<%= javascript_include_tag :defaults, "nested_form" %>

Usage

Imagine you have a Project model that has_many :tasks. To be able to use this gem, you'll need to add accepts_nested_attributes_for :tasks to your Project model. If you wish to allow the nested objects to be destroyed, then add the :allow_destroy => true option to that declaration. See the accepts_nested_attributes_for documentation for details on all available options.

This will create a tasks_attributes= method, so you may need to add it to the attr_accessible array (attr_accessible :tasks_attributes).

Then use the nested_form_for helper method to enable the nesting.

<%= nested_form_for @project do |f| %>

You will then be able to use link_to_add and link_to_remove helper methods on the form builder in combination with fields_for to dynamically add/remove nested records.

<%= f.fields_for :tasks do |task_form| %>
  <%= task_form.text_field :name %>
  <%= task_form.link_to_remove "Remove this task" %>
<% end %>
<p><%= f.link_to_add "Add a task", :tasks %></p>

In order to choose how to handle, after validation errors, fields that are marked for destruction, the marked_for_destruction class is added on the div if the object is marked for destruction.

Strong Parameters

For Rails 4 or people using the "strong_parameters" gem, here is an example:

params.require(:project).permit(:name, tasks_attributes: [:id, :name, :_destroy])

The :id is to make sure you do not end up with a whole lot of tasks.

The :_destroy must be there so that we can delete tasks.

SimpleForm and Formtastic Support

Use simple_nested_form_for or semantic_nested_form_for for SimpleForm and Formtastic support respectively.

Partials

It is often desirable to move the nested fields into a partial to keep things organized. If you don't supply a block to fields_for it will look for a partial and use that.

<%= f.fields_for :tasks %>

In this case it will look for a partial called "task_fields" and pass the form builder as an f variable to it.

Specifying a Target for Nested Fields

By default, link_to_add appends fields immediately before the link when clicked. This is not desirable when using a list or table, for example. In these situations, the "data-target" attribute can be used to specify where new fields should be inserted.

<table id="tasks">
  <%= f.fields_for :tasks, :wrapper => false do |task_form| %>
    <tr class="fields">
      <td><%= task_form.text_field :name %></td>
      <td><%= task_form.link_to_remove "Remove this task" %></td>
    </tr>
  <% end %>
</table>
<p><%= f.link_to_add "Add a task", :tasks, :data => { :target => "#tasks" } %></p>

Note that the :data option above only works in Rails 3.1+. For Rails 3.0 and below, the following syntax must be used.

<p><%= f.link_to_add "Add a task", :tasks, "data-target" => "#tasks" %></p>

JavaScript events

Sometimes you want to do some additional work after element was added or removed, but only after DOM was really modified. In this case simply listening for click events on 'Add new'/'Remove' link won't reliably work, because your code and code that inserts/removes nested field will run concurrently.

This problem can be solved, because after adding or removing the field a set of custom events is triggered on this field. Using form example from above, if you click on the "Add a task" link, nested:fieldAdded and nested:fieldAdded:tasks will be triggered, while nested:fieldRemoved and nested:fieldRemoved:tasks will be triggered if you click "Remove this task" then.

These events bubble up the DOM tree, going through form element, until they reach the document. This allows you to listen for the event and trigger some action accordingly. Field element, upon which action was made, is passed along with the event object. In jQuery you can access it via event.field, in Prototype the same field will be in event.memo.field.

For example, you have a date input in a nested field and you want to use jQuery datepicker for it. This is a bit tricky, because you have to activate datepicker after field was inserted.

jQuery

$(document).on('nested:fieldAdded', function(event){
  // this field was just inserted into your form
  var field = event.field;
  // it's a jQuery object already! Now you can find date input
  var dateField = field.find('.date');
  // and activate datepicker on it
  dateField.datepicker();
})

Prototype

document.observe('nested:fieldAdded', function(event){
  var field = event.memo.field;
  // it's already extended by Prototype
  var dateField = field.down('.date');
  dateField.datepicker();
})

Second type of event (i.e. nested:fieldAdded:tasks) is useful then you have more than one type of nested fields on a form (i.e. tasks and milestones) and want to distinguish, which exactly was added/deleted.

See also how to limit max count of nested fields

Enhanced jQuery JavaScript template

You can override default behavior of inserting new subforms into your form. For example:

window.nestedFormEvents.insertFields = function(content, assoc, link) {
  return $(link).closest('form').find(assoc + '_fields').append($(content));
}

Contributing

If you have any issues with Nested Form not addressed above or in the example project, please add an issue on GitHub or fork the project and send a pull request. To run the specs:

bundle install
bundle exec rake spec:install
bundle exec rake db:migrate
bundle exec rake spec:all

See available rake tasks using bundle exec rake -T.

Special Thanks

This gem was originally based on the solution by Tim Riley in his complex-form-examples fork.

Thank you Andrew Manshin for the Rails 3 transition, Andrea Singh for converting to a gem and Peter Giacomo Lombardo for Prototype support.

Andrea also wrote a great blog post on the internal workings of this gem.

Thanks Pavel Forkert for the SimpleForm and Formtastic support.

More Repositories

1

cancan

Authorization Gem for Ruby on Rails.
Ruby
6,283
star
2

ruby-warrior

Game written in Ruby for learning Ruby and artificial intelligence.
Ruby
3,776
star
3

letter_opener

Preview mail in the browser instead of sending.
Ruby
3,633
star
4

dotfiles

config files for zsh, bash, completions, gem, git, irb, rails
Shell
2,288
star
5

nifty-generators

A collection of useful Rails generator scripts.
Ruby
1,983
star
6

private_pub

Handle pub/sub messaging through private channels in Rails using Faye.
Ruby
865
star
7

railscasts-episodes

NOT MAINTAINED. See README.
Ruby
845
star
8

railscasts

railscasts.com in open source (outdated).
Ruby
760
star
9

populator

Mass populate an Active Record database.
Ruby
392
star
10

complex-form-examples

Various ways to handle multi-model forms in Rails.
Ruby
304
star
11

trusted-params

Rails plugin for overriding attr_accessible protection.
Ruby
149
star
12

mustard

Simple "must" expectations for tests and specs in Ruby.
Ruby
144
star
13

govsgo

Rails 3 app for playing the board game Go online.
Ruby
141
star
14

xapit

High level Ruby library for interacting with Xapian, a full text search engine.
Ruby
140
star
15

rails-templates

Template scripts for creating new rails applications.
Ruby
134
star
16

cocoa-web-app-example

A Cocoa application to demonstrate the interaction between Objective-C and JavaScript in a WebView.
Objective-C
96
star
17

importex

Import an Excel file using Ruby.
Ruby
90
star
18

uniquify

Generate a unique, random token for Active Record.
Ruby
88
star
19

textmate-themes

My TextMate themes (includes Railscasts theme)
70
star
20

acts-as-list

NOT MAINTAINED. Gem version of acts_as_list Rails plugin.
Ruby
65
star
21

abingo

Fork of A/Bingo plugin for Rails.
Ruby
55
star
22

railscasts-scripts

Scripts used internally when producing RailsCasts
Ruby
52
star
23

scope-builder

Build up named scopes conditionally.
Ruby
51
star
24

rmov

Ruby wrapper for the QuickTime C API.
C
48
star
25

render-caching

Cache render calls in Rails controllers.
Ruby
45
star
26

enlighten

Interactive ruby debugger in the browser.
Ruby
42
star
27

static_actions

Rails plugin to quickly make named routes for non-RESTful actions.
Ruby
39
star
28

searchify

Rails plugin to add extra searching functionality to models.
Ruby
37
star
29

selenium-on-rails

This repo is no longer maintained, see the official repository by paytonrules.
JavaScript
34
star
30

ryan-on-rails.tmbundle

Some TextMate snippets I use when working with Ruby and Rails.
26
star
31

dailystamp

Source code for my Rails Rumble 2009 submission
Ruby
23
star
32

url_formatter

Format and validate a URL in Active Record. Example gem for RailsCasts.
Ruby
18
star
33

association-freezer

Freeze a belongs_to association in Active Record.
Ruby
17
star
34

admiteer

Rails Rumble 2007 project by Jack Canty, Kelli Shaver, and Ryan Bates
17
star
35

todo-list.tmbundle

A simple TextMate bundle to manage a todo lists.
14
star
36

xapit-sync

Rails plugin to automatically reload a Xapian database when models change.
Ruby
13
star
37

myideadrawer

Rails Rumble 2008 entry by Ryan Bates and Kelli Shaver
Ruby
13
star
38

blog-screencast

Example blog application built in the offical 15 minute Rails screencast.
Ruby
12
star
39

ryan-bates.tmbundle

Miscellaneous commands and snippets I use in TextMate.
11
star
40

advent-2022

Advent of Code in Elixir
Elixir
11
star
41

maestro

Piano exercise game written in MacRuby.
Ruby
11
star
42

vscode-railscasts-theme

RailsCasts Theme for VS Code
7
star
43

ryanb.github.io

Personal site for Ryan Bates
5
star
44

vscode-erb-syntax

ERB Syntax for VS Code
5
star
45

xapit-server

Rack server for interacting with a Xapian database remotely through Xapit.
Ruby
4
star
46

swapper

Ruby script for swapping two elements on a line (to be used in text editors).
3
star
47

bookmarklets

JavaScript
1
star
48

wallaby-rails-7-1-2

Example Rails 7.1.2 app with Wallaby
Ruby
1
star