No commit activity in last 3 years
No release in over 3 years
Dynamic association fieldsets without pain.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

 Project Readme

DynamicFieldsFor

Version Build Climate Coverage

DynamicFieldsFor is a Rails plugin which provides the dynamic association fieldsets to your forms without pain. And it does nothing else.

The main features are:

  • Doesn't break the HTML layout - no wrappers, additional divs etc;
  • Works with fields block, i.e. doesn't require the separated partial for them;
  • Doesn't provide new form helpers, but extends the existing one;
  • Simple and predictable interface and behavior;
  • Doesn't require any special HTML entities inside templates;
  • Supports Simple Form.
  • Supports not ActiveRecord models
  • Supports nested dynamic fields

Alternatives

Dependencies

For older versions of ruby and rails - please use gem version 1.1.0.

Getting started

Add to your Gemfile:

  gem 'dynamic-fields-for'

Run the bundle command to install it.

Add to app/assets/javascripts/application.js:

//= require dynamic-fields-for

Usage

Lets say that we have the models:

class User < ActiveRecord::Base
  has_many :roles
end

class Role < ActiveRecod::Base
  belongs_to :user

  validates :user, presence: true
end

First, apply inverse_of to User's :roles associations, otherwise there is no chance to pass the validation of Role's user presence on user creation:

class User < ActiveRecord::Base
  has_many :roles, inverse_of: :user
end

Add to User model:

accepts_nested_attributes_for :roles, allow_destroy: true

Skip allow_destroy definition if you don't need to use remove_fields_link helper).

Take care about strong parameters in controller like this:

params.require(:user).permit(roles_attributes: [:id, :_destroy])

It is important to permit id role's parameter, don't miss it. As for _destroy, skip it if you don't need to use remove_fields_link helper.

Then, in view:

= form_for resource do |f|
  = f.text_field :user_name

  = f.fields_for :roles, dynamic: true do |rf|
    = rf.text_field :role_name
    = rf.remove_fields_link 'Remove role'

  = f.add_fields_link :roles, 'Add role'

  = f.submit

DynamicFieldsFor supports SimpleForm:

= simple_form_for resource do |f|
  = f.input :user_name

  = f.simple_fields_for :roles, dynamic: true do |rf|
    = rf.input :role_name
    = rf.remove_fields_link 'Remove role'

  = f.add_fields_link :roles, 'Add role'

  = f.submit

Not ActiveRecord models

To use DynamicFieldsFor with not ActiveRecord, it's necessary to define two methods in your model, {association}_soft_build and {association}_attributes=:

class EmailForm
  include ActiveAttr::Model

  attribute :recipients, type: Object, default: []

  def recipients_attributes=(attributes)
    self.recipients = attributes.values.map{ |attrs| recipients_soft_build(attrs) }
  end

  def recipients_soft_build(attrs = {})
    Recipient.new(attrs)
  end

end

class Recipient
  include ActiveAttr::Model

  attribute :email

  validates :email,  presence: true
end

Template will stay to be as usual:

= form_for resource do |f|
  = f.fields_for :recipients, dynamic: true do |rf|
    = rf.text_field :email
    = rf.remove_fields_link 'Remove recipient'

  = f.add_fields_link :recipients, 'Add recipient'

  = f.submit

JavaScript events

There are the events which will be triggered on add_fields_link click, in actual order:

  • dynamic-fields:before-add-into touched to dynamic fields parent node;
  • dynamic-fields:after-add touched to each first-level elements which were inserted;
  • dynamic-fields:after-add-into touched to dynamic fields parent node;

Like that, these events will be triggered on add_fields_link click, in actual order:

  • dynamic-fields:before-remove-from touched to dynamic fields parent node;
  • dynamic-fields:before-remove touched to each first-level elements which are going to be removed;
  • dynamic-fields:after-remove-from touched to dynamic fields parent node;

Typical callback for dynamic fields parent node looks like:

$(document).on('dynamic-fields:after-add-into', function(event){
  $(event.target).find('li').order();
})

As for first-level elements, compatible callbacks will be triggered to each of them. To deal with this, use $.find2 javascript helper, which provided by DynamicFieldsFor:

$('#some_id').find2('.some_class');
// doing the same as...
$('#some_id').find('.some_class').add($('#some_id').filter('.some_class'));

Typical event callback first-level elements should look like:

$(document).on('dynamic-fields:after-add', function(event){
  $(event.target).find2('.datepicker').datetimepicker();
})

License

MIT License. Copyright (c) 2015 Sergey Tokarenko