Coder Social home page Coder Social logo

nathanvda / cocoon Goto Github PK

View Code? Open in Web Editor NEW
3.1K 3.1K 383.0 375 KB

Dynamic nested forms using jQuery made easy; works with formtastic, simple_form or default forms

Home Page: http://github.com/nathanvda/cocoon

License: MIT License

Ruby 79.84% JavaScript 16.07% HTML 4.09%

cocoon's Introduction

cocoon

Build Status

Cocoon makes it easier to handle nested forms.

Nested forms are forms that handle nested models and attributes in one form; e.g. a project with its tasks or an invoice with its line items.

Cocoon is form builder-agnostic, so it works with standard Rails, Formtastic, or SimpleForm. It is compatible with rails 3, 4 and 5.

This project is not related to Apache Cocoon.

Prerequisites

This gem depends on jQuery, so it's most useful in a Rails project where you are already using jQuery. Furthermore, I would advise you to use either Formtastic or SimpleForm.

Installation

Inside your Gemfile add the following:

gem "cocoon"

Please note that for rails 4 you will need at least v1.2.0 or later.

Rails 6/Webpacker

Add the companion package

yarn add @nathanvda/cocoon 

and then in your app/javascripts/packs/application.js you should add

require("jquery")
require("@nathanvda/cocoon")

Note: there are alternative npm packages, which might better suit your needs. E.g. some offer the cocoon functionality without using jquery (search for cocoon + vanilla --I found three packages on npm already). Obviously you are free to use those, however the code samples in this README will (still) rely on jquery.

Rails 3.1+/Rails 4/Rails 5

Add the following to application.js so it compiles to the asset pipeline:

//= require cocoon

Rails 3.0.x

If you are using Rails 3.0.x, you need to run the installation task (since rails 3.1 this is no longer needed):

rails g cocoon:install

This will install the Cocoon JavaScript file. In your application layout, add the following below the default javascripts:

= javascript_include_tag :cocoon

Basic Usage

Suppose you have a Project model:

rails g scaffold Project name:string description:string

And a project has many tasks:

rails g model Task description:string done:boolean project:belongs_to

Your models are associated like this:

class Project < ActiveRecord::Base
  has_many :tasks, inverse_of: :project
  accepts_nested_attributes_for :tasks, reject_if: :all_blank, allow_destroy: true
end

class Task < ActiveRecord::Base
  belongs_to :project
end

Rails 5 Note: since rails 5 a belongs_to relation is by default required. While this absolutely makes sense, this also means associations have to be declared more explicitly. When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :project

Now we want a project form where we can add and remove tasks dynamically. To do this, we need the fields for a new or existing task to be defined in a partial named _task_fields.html.

Strong Parameters Gotcha

To destroy nested models, rails uses a virtual attribute called _destroy. When _destroy is set, the nested model will be deleted. If the record is persisted, rails performs id field lookup to destroy the real record, so if id wasn't specified, it will treat current set of parameters like a parameters for a new record.

When using strong parameters (default in rails 4), you need to explicitly add both :id and :_destroy to the list of permitted parameters.

E.g. in your ProjectsController:

  def project_params
    params.require(:project).permit(:name, :description, tasks_attributes: [:id, :description, :done, :_destroy])
  end

Has One Gotcha

If you have a has_one association, then you (probably) need to set force_non_association_create: true on link_to_add_association

If you don't do this, then rails will automatically delete your existing association when you view the edit form!

More details in the wiki

Examples

Cocoon's default configuration requires link_to_add_association and associated partials to be properly wrapped with elements. The examples below illustrate simple layouts.

Please note these examples rely on the haml gem (click here for the default erb views).

Formtastic

In our projects/_form partial we'd write:

= semantic_form_for @project do |f|
  = f.inputs do
    = f.input :name
    = f.input :description
    %h3 Tasks
    #tasks
      = f.semantic_fields_for :tasks do |task|
        = render 'task_fields', f: task
      .links
        = link_to_add_association 'add task', f, :tasks
    = f.actions do
      = f.action :submit

And in our _task_fields partial we'd write:

.nested-fields
  = f.inputs do
    = f.input :description
    = f.input :done, as: :boolean
    = link_to_remove_association "remove task", f

The example project cocoon_formtastic_demo demonstrates this.

SimpleForm

In our projects/_form partial we'd write:

= simple_form_for @project do |f|
  = f.input :name
  = f.input :description
  %h3 Tasks
  #tasks
    = f.simple_fields_for :tasks do |task|
      = render 'task_fields', f: task
    .links
      = link_to_add_association 'add task', f, :tasks
  = f.submit

In our _task_fields partial we write:

.nested-fields
  = f.input :description
  = f.input :done, as: :boolean
  = link_to_remove_association "remove task", f

The example project cocoon_simple_form_demo demonstrates this.

Standard Rails forms

In our projects/_form partial we'd write:

= form_for @project do |f|
  .field
    = f.label :name
    %br
    = f.text_field :name
  .field
    = f.label :description
    %br
    = f.text_field :description
  %h3 Tasks
  #tasks
    = f.fields_for :tasks do |task|
      = render 'task_fields', f: task
    .links
      = link_to_add_association 'add task', f, :tasks
  = f.submit

In our _task_fields partial we'd write:

.nested-fields
  .field
    = f.label :description
    %br
    = f.text_field :description
  .field
    = f.check_box :done
    = f.label :done
  = link_to_remove_association "remove task", f

How it works

Cocoon defines two helper functions:

link_to_add_association

This function adds a link to your markup that, when clicked, dynamically adds a new partial form for the given association. This should be called within the form builder.

link_to_add_association takes four parameters:

  • name: the text to show in the link
  • f: the form builder
  • association: the name of the association (plural) of which a new instance needs to be added (symbol or string).
  • html_options: extra html-options (see link_to There are some special options, the first three allow to control the placement of the new link-data:
    • data-association-insertion-traversal : the jquery traversal method to allow node selection relative to the link. closest, next, children, etc. Default: absolute selection
    • data-association-insertion-node : the jquery selector of the node as string, or a function that takes the link_to_add_association node as the parameter and returns a node. Default: parent node
    • data-association-insertion-method : jquery method that inserts the new data. before, after, append, prepend, etc. Default: before
    • data-association-insertion-position : old method specifying where to insert new data.
      • this setting still works but data-association-insertion-method takes precedence. may be removed in a future version.
    • partial: explicitly declare the name of the partial that will be used
    • render_options : options passed through to the form-builder function (e.g. simple_fields_for, semantic_fields_for or fields_for). If it contains a :locals option containing a hash, that is handed to the partial.
    • wrap_object : a proc that will allow to wrap your object, especially useful if you are using decorators (e.g. draper). See example lower.
    • force_non_association_create: if true, it will not create the new object using the association (see lower)
    • form_name : the name of the form parameter in your nested partial. By default this is f.
    • count : the number of nested items to insert at once. Default 1.

Optionally, you can omit the name and supply a block that is captured to render the link body (if you want to do something more complicated).

:render_options

Inside the html_options you can add an option :render_options, and the containing hash will be handed down to the form builder for the inserted form.

When using Twitter Bootstrap and SimpleForm together, simple_fields_for needs the option wrapper: 'inline' which can be handed down as follows:

(Note: In certain newer versions of simple_form, the option to use is wrapper: 'bootstrap'.)

= link_to_add_association 'add something', f, :something,
    render_options: { wrapper: 'inline' }

To specify locals that needed to handed down to the partial:

= link_to_add_association 'add something', f, :something,
    render_options: {locals: { sherlock: 'Holmes' }}

:partial

To override the default partial name, e.g. because it shared between multiple views:

= link_to_add_association 'add something', f, :something,
    partial: 'shared/something_fields'

:wrap_object

If you are using decorators, the normal instantiation of the associated object will not be enough. You actually want to generate the decorated object.

A simple decorator would look like:

class CommentDecorator
  def initialize(comment)
    @comment = comment
  end

  def formatted_created_at
    @comment.created_at.to_formatted_s(:short)
  end

  def method_missing(method_sym, *args)
    if @comment.respond_to?(method_sym)
      @comment.send(method_sym, *args)
    else
      super
    end
  end
end

To use this:

= link_to_add_association('add something', @form_obj, :comments,
    wrap_object: Proc.new {|comment| CommentDecorator.new(comment) })

Note that the :wrap_object expects an object that is callable, so any Proc will do. So you could as well use it to do some fancy extra initialisation (if needed). But note you will have to return the (nested) object you want used. E.g.

= link_to_add_association('add something', @form_obj, :comments,
    wrap_object: Proc.new { |comment| comment.name = current_user.name; comment })

:force_non_association_create

In normal cases we create a new nested object using the association relation itself. This is the cleanest way to create a new nested object.

This used to have a side-effect: for each call of link_to_add_association a new element was added to the association. This is no longer the case.

For backward compatibility we keep this option for now. Or if for some specific reason you would really need an object to be not created on the association, for example if you did not want after_add callback to be triggered on the association.

Example use:

= link_to_add_association('add something', @form_obj, :comments,
    force_non_association_create: true)

By default :force_non_association_create is false.

link_to_remove_association

This function will add a link to your markup that, when clicked, dynamically removes the surrounding partial form. This should be placed inside the partial _<association-object-singular>_fields.

It takes three parameters:

  • name: the text to show in the link
  • f: referring to the containing form-object
  • html_options: extra html-options (see link_to)

Optionally you could also leave out the name and supply a block that is captured to give the name (if you want to do something more complicated).

Optionally, you can add an html option called wrapper_class to use a different wrapper div instead of .nested-fields. The class should be added without a preceding dot (.).

Note: the javascript behind the generated link relies on the presence of a wrapper class (default .nested-fields) to function correctly.

Example:

= link_to_remove_association('remove this', @form_obj,
  { wrapper_class: 'my-wrapper-class' })

Callbacks (upon insert and remove of items)

On insertion or removal the following events are triggered:

  • cocoon:before-insert: called before inserting a new nested child, can be canceled
  • cocoon:after-insert: called after inserting
  • cocoon:before-remove: called before removing the nested child, can be canceled
  • cocoon:after-remove: called after removal

To listen to the events in your JavaScript:

  $('#container').on('cocoon:before-insert', function(e, insertedItem, originalEvent) {
    // ... do something
  });

...where e is the event and the second parameter is the inserted or removed item. This allows you to change markup, or add effects/animations (see example below). originalEvent is also passed and references the event that triggered an insertion or removal (e.g. the click event on the link to add an association)

If in your view you have the following snippet to select an owner:

#owner
  #owner_from_list
    = f.association :owner, collection: Person.all(order: 'name'), prompt: 'Choose an existing owner'
  = link_to_add_association 'add a new person as owner', f, :owner

This will either let you select an owner from the list of persons, or show the fields to add a new person as owner.

The callbacks can be added as follows:

$(document).ready(function() {
    $('#owner')
      .on('cocoon:before-insert', function() {
        $("#owner_from_list").hide();
        $("#owner a.add_fields").hide();
      })
      .on('cocoon:after-insert', function() {
        /* ... do something ... */
      })
      .on("cocoon:before-remove", function() {
        $("#owner_from_list").show();
        $("#owner a.add_fields").show();
      })
      .on("cocoon:after-remove", function() {
        /* e.g. recalculate order of child items */
      });

    // example showing manipulating the inserted/removed item

    $('#tasks')
      .on('cocoon:before-insert', function(e,task_to_be_added) {
        task_to_be_added.fadeIn('slow');
      })
      .on('cocoon:after-insert', function(e, added_task) {
        // e.g. set the background of inserted task
        added_task.css("background","red");
      })
      .on('cocoon:before-remove', function(e, task) {
        // allow some time for the animation to complete
        $(this).data('remove-timeout', 1000);
        task.fadeOut('slow');
      });
});

Note that for the callbacks to work there has to be a surrounding container to which you can bind the callbacks.

When adding animations and effects to make the removal of items more interesting, you will also have to provide a timeout. This is accomplished by the following line:

$(this).data('remove-timeout', 1000);

You could also immediately add this to your view, on the .nested-fields container (or the wrapper_class element you are using).

Canceling a Callback

You can cancel an action from occurring, either an insertion or removal, within the before-<action> callback by calling event.preventDefault() anywhere within the callback.

For example:

  $('#container').on('cocoon:before-insert', function(event, insertedItem) {
    var confirmation = confirm("Are you sure?");
    if( confirmation === false ){
      event.preventDefault();
    }
  });

Control the Insertion Behaviour

The default insertion location is at the back of the current container. But we have added two data- attributes that are read to determine the insertion-node and -method.

For example:

$(document).ready(function() {
    $("#owner a.add_fields").
      data("association-insertion-method", 'before').
      data("association-insertion-node", 'this');
});

The association-insertion-node will determine where to add it. You can choose any selector here, or specify this. Also, you can pass a function that returns an arbitrary node. The default is the parent-container, if you don't specify anything.

The association-insertion-method will determine where to add it in relation with the node. Any jQuery DOM Manipulation method can be set but we recommend sticking to any of the following: before, after, append, prepend. It is unknown at this time what others would do.

The association-insertion-traversal will allow node selection to be relative to the link.

For example:

$(document).ready(function() {
    $("#owner a.add_fields").
      data("association-insertion-method", 'append').
      data("association-insertion-traversal", 'closest').
      data("association-insertion-node", '#parent_table');
});

(if you pass association-insertion-node as a function, this value will be ignored)

Note, if you want to add templates to the specific location which is:

  • not a direct parent or sibling of the link
  • the link appears multiple times - for instance, inside a deeply nested form

you need to specify association-insertion-node as a function.

For example, suppose Task has many SubTasks in the Example, and have subtask forms like the following.

.row
  .col-lg-12
    .add_sub_task= link_to_add_association 'add a new sub task', f, :sub_tasks
.row
  .col-lg-12
    .sub_tasks_form
      fields_for :sub_tasks do |sub_task_form|
        = render 'sub_task_fields', f: sub_task_form

Then this will do the thing.

$(document).ready(function() {
    $(".add_sub_task a").
      data("association-insertion-method", 'append').
      data("association-insertion-node", function(link){
        return link.closest('.row').next('.row').find('.sub_tasks_form')
      });
});

Partial

If no explicit partial name is given, cocoon looks for a file named _<association-object_singular>_fields. To override the default partial use the :partial option.

For the JavaScript to behave correctly, the partial should start with a container (e.g. div) of class .nested-fields, or a class of your choice which you can define in the link_to_remove_association method.

There is no limit to the amount of nesting, though.

I18n

As you seen in previous sections, the helper method link_to_add_association treats the first parameter as a name. Additionally, if it's skipped and the form object is passed as the first one, then Cocoon names it using I18n.

It allows to invoke helper methods like this:

= link_to_add_association form_object, :tasks
= link_to_remove_association form_object

instead of:

= link_to_add_association "Add task", form_object, :tasks
= link_to_remove_association "remove task", form_object

Cocoon uses the name of association as a translations scope key. If custom translations for association is not present it fallbacks to default name. Example of translations tree:

en:
  cocoon:
    defaults:
      add: "Add record"
      remove: "Remove record"
    tasks:
      add: "Add new task"
      remove: "Remove old task"

Note that link_to_remove_association does not require association name as an argument. In order to get correct translation key, Cocoon tableizes class name of the target object of form builder (form_object.object from previous example).

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Contributors

The list of contributors just keeps on growing. Check it out! I would really really like to thank all of them. They make cocoon more awesome every day. Thanks.

Todo

  • add more sample relations: has_many :through, belongs_to, ...
  • improve the tests (test the javascript too)(if anybody wants to lend a hand ...?)

Copyright

Copyright (c) 2010 Nathan Van der Auwera. See LICENSE for details.

Not Related To Apache Cocoon

Please note that this project is not related to the Apache Cocoon web framework project.

Apache Cocoon, Cocoon, and Apache are either registered trademarks or trademarks of the Apache Software Foundation in the United States and/or other countries.

cocoon's People

Contributors

ahmozkya avatar anthonylebrun avatar brixen avatar curiouscurmudgeon avatar dnagir avatar dpsk avatar entretechno-jeremiah avatar f3ndot avatar finist avatar fl00r avatar gdlx avatar gnclmorais avatar killthekitten avatar liooo avatar mfrederickson avatar michaelkoper avatar nathanphilliber avatar nathanvda avatar nick-desteffen avatar niuage avatar p8 avatar patricklindsay avatar paulcookie avatar rradonic avatar sebastianedwards avatar shanecurcuru avatar simi avatar viliusluneckas avatar vongruenigen avatar yoavmatchulsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cocoon's Issues

cocoon not working on associations with irregular plurals

I have a model Person in my application and cocoon won't work with a has_many association on another model using the irregular plural :people.

I am afraid this is the case for all associations using a plural that is not equal to the singular or where the singular is not a prefix of the plural. What happens in these cases is that the regular expression used in the javascript code to inject a random id for new records does not match.

I am not sure how to fix this properly (hence no pull request, sorry). A quick fix would be to not store the singular name of the association as the data-association attribute of the add link. But this would mean you would have to rename most of the partials which in turn would break most existing applications.

Implementing inflections in javascript does not seem to be a good idea either :)

Maybe you could come up with a clean solution. If not, I might give it a try anyways.

Rails 3.2 and 'can't convert nil into Hash'

Hi,

I'm getting a 'can't convert nil into Hash' error on my link_to_add_association line, in a Rails 3.2 app.

Here's the top of the stack trace where the error is occurring:

cocoon (1.0.18) lib/cocoon/view_helpers.rb:39:in merge' cocoon (1.0.18) lib/cocoon/view_helpers.rb:39:inblock in render_association'

link_to_remove_association works fine in the partial, and the partial for my association renders fine, but everything blows up when I try to use link_to_add_association.

Here's what the form looks like: https://gist.github.com/2332370

Issues with triple nesting

Hey, love the library, but having some issues with nesting on this project
Don't think it's necessarily an issue with the library, but my head is spinning so thought I'd post in case you had any ideas!

Here's the hierarchy:

Poll (form) has_many Questions (form) has_many Options (Form) has_one Follow-up, class: Question (Form) has_many Options (Form)

UI renders fine, but issues arise editing objects with followups, or creating items with follow-up-options (the two deepest levels of nesting)

1- The most serious issue, When creating a poll: Unable to create more than one follow-up option. Everything looks fine in the UI, but only one follow-up-option gets posted and saved

When attempting to create a poll with the following structure

    Question, text: "First question" type "MULTI" 
        option, label: apples, value: a
        option, label: bananas, value: b
            Follow-up question, text: "Are you sure?", type: "YN"
                option: yes
                option: no

This is what gets posted

    Started POST "/polls" for 127.0.0.1 at 2012-05-24 18:42:07 -0700
    Processing by PollsController#create as HTML
    Parameters: {
       "utf8"   =>"✓",
       "authenticity_token"   =>"geW3nvwDaEftZyJUaAkmb69iD0aJJe/WT1/ynV+pl+M=",
       "poll"   =>   {
          "questions_attributes"      =>      {
             "1337909867131"         =>         {
                "text"            =>"First question",
                "_destroy"            =>"",
                "question_type"            =>"MULTI",
                "options_attributes"            =>            {
                   "1337909878928"               =>               {
                      "text"                  =>"apples",
                      "_destroy"                  =>"",
                      "value"                  =>"a"
                   },
                   "1337909884148"               =>               {
                      "text"                  =>"bananas",
                      "_destroy"                  =>"",
                      "value"                  =>"b",
                      "follow_up_attributes"                  =>                  {
                         "text"                     =>"Are you sure?",
                         "question_type"                     =>"YN",
                         "_destroy"                     =>"",
                         "options_attributes"                     =>                     {
                            "1337909884148"                        =>                        {
                               "text"                           =>"no",
                               "value"                           =>"n",
                               "_destroy"                           =>""
                            }
                         }
                      }
                   }
                }
             }
          },
          ... snip...
    }     

2- When editing: Can't edit any poll that has follow-ups. When attempting to edit, it seems follow-ups are being deleted. In the below log, questions_all grabs all the questions for a poll, including for any followups

Check this out

  1.9.3p125 :187 > Poll.find(57).questions_all
   => [#<Question id: 72, text: "where?", poll_id: 57, question_type: "OPEN", created_at: "2012-05-24 22:51:15", updated_at: "2012-05-24 22:51:15", sequence: nil, parent_option_id: nil>, #<Question id: 70, text: "yes or no?", poll_id: 57, question_type: "OPEN", created_at: "2012-05-24 22:51:15", updated_at: "2012-05-25 01:23:36", sequence: nil, parent_option_id: nil>, #<Question id: 73, text: "aaaaa", poll_id: nil, question_type: "OPEN", created_at: "2012-05-25 01:23:36", updated_at: "2012-05-25 01:23:36", sequence: nil, parent_option_id: 55>] 

  1.9.3p125 :188 > Poll.find(57).options_all
   => [#<Option id: 55, text: "yes", question_id: 70, value: "yes", follow_up_id: nil, created_at: "2012-05-24 22:51:15", updated_at: "2012-05-24 22:51:15">, #<Option id: 56, text: "no", question_id: 70, value: "no", follow_up_id: nil, created_at: "2012-05-24 22:51:15", updated_at: "2012-05-24 22:51:15">] 

  polls/57/edit with no changes, just hit "update"

    ActiveRecord::RecordNotFound in PollsController#update

    Couldn't find Question with ID=73 for Option with ID=55

  then...

  1.9.3p125 :189 > reload!
  Reloading...
   => true 
  1.9.3p125 :190 > Poll.find(57).questions_all
   => [#<Question id: 72, text: "where?", poll_id: 57, question_type: "OPEN", created_at: "2012-05-24 22:51:15", updated_at: "2012-05-24 22:51:15", sequence: nil, parent_option_id: nil>, #<Question id: 70, text: "yes or no?", poll_id: 57, question_type: "OPEN", created_at: "2012-05-24 22:51:15", updated_at: "2012-05-25 01:23:36", sequence: nil, parent_option_id: nil>] 
  1.9.3p125 :191 > Question.find(73)
    Question Load (0.7ms)  SELECT "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT 1  [["id", 73]]
  ActiveRecord::RecordNotFound: Couldn't find Question with id=73

I'm pretty much out of ideas, my next step was to create a separate follow-ups table (or try using Single Table Inheritance to emulate it) and see if that fixed anything...
Thanks again!

infinite partial rendering

Not sure what I did but it keep trying to render the partial over and over and over, I have to stop the server.

It was working at one point:

<%= form_for @person, :html => { :class => 'form-horizontal' } do |f| %>

<%= controller.action_name.capitalize %> Person
<%= f.label :radio_show, :class => 'control-label' %>
<%= f.check_box :radio_show, :class => 'checkbox'%>
<%= f.label :maintenance, :class => 'control-label' %>
<%= f.check_box :maintenance, :class => 'checkbox'%>
<%= f.label :name, :class => 'control-label' %>
<%= f.text_field :name, :class => 'text_field' %>
    <div class="control-group">
      <%= f.label :age, :class => 'control-label' %>
      <div class="controls">
        <%= f.text_field :age, :class => 'text_field' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :weight, :class => 'control-label' %>
      <div class="controls">
        <%= f.text_field :weight, :class => 'text_field' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :height, :class => 'control-label' %>
      <div class="controls">
        <%= f.collection_select :height, (50..79).to_a, :to_i, :inches_to_feet %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :shellfish_allergy, :class => 'control-label' %>
      <div class="controls">
        <%= f.check_box :shellfish_allergy, :class => 'checkbox' %> - <%= ' allergic to shellfish?' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :height_loss, :class => 'control-label' %>
      <div class="controls">
        <%= f.check_box :height_loss, :class => 'checkbox' %> - <%= 'Lost any height since you were 18?' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :gluten_intolerance, :class => 'control-label' %>
      <div class="controls">
        <%= f.check_box :gluten_intolerance, :class => 'checkbox' %> - <%= 'ezcema, dermatitis, ever had asthma' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :date_of_problema, :class => 'control-label' %>
      <div class="controls">
        <%= f.datepicker :date_of_problema, :class => 'date_select' %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :note, :class => 'control-label' %>
      <div class="controls">
        <%= f.text_area :note, :class => 'text_field', :size => '50X20'%>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :conditions_tokens, 'Conditions', :class => 'control-label' %>
      <div class="controls">
        <%= f.text_field :conditions_tokens, 'data-pre' => @person.conditions.map(&:attributes).to_json %>
      </div>
    </div>

    <%# = render :partial => 'medicated/medications_taking' %>

    <div class="control-group">
      <%= f.label :mighty_90_amounts, :class => 'control-label' %>
      <div class="controls">
        <%= f.select :mighty_90_amounts, Product.mighty_90_amounts %>
      </div>
    </div>
</div>          
<div class="span5">
  <%= f.fields_for :solutions do |s| %>
    <%= link_to_add_association 'Add Solution', f, :solutions, :render_options => {:wrapper => 'inline' } %>
    <%= render 'solution_fields', :f => s %>
  <% end %>
</div>

<div class="form-actions">
    <%= f.submit nil, :class => 'btn btn-primary' %>
  <%= link_to 'Cancel', people_path, :class => 'btn' %>
</div>
<% end %>

LOG truncated:

Started GET "/people/new" for 127.0.0.1 at 2012-05-19 22:07:08 -0500
Processing by PeopleController#new as HTML
Rendered people/_solution_fields.html.erb (4.0ms)
Rendered people/_solution_fields.html.erb (25.8ms)
Rendered people/_solution_fields.html.erb (3.8ms)
Rendered people/_solution_fields.html.erb (3.5ms)
Rendered people/_solution_fields.html.erb (3.4ms)
Rendered people/_solution_fields.html.erb (25.3ms)
Rendered people/_solution_fields.html.erb (3.4ms)
Rendered people/_solution_fields.html.erb (3.3ms)
Rendered people/_solution_fields.html.erb (25.0ms)
Rendered people/_solution_fields.html.erb (3.6ms)
Rendered people/_solution_fields.html.erb (3.4ms)
Rendered people/_solution_fields.html.erb (25.3ms)
Rendered people/_solution_fields.html.erb (3.6ms)
Rendered people/_solution_fields.html.erb (3.3ms)
Rendered people/_solution_fields.html.erb (25.4ms)
Rendered people/_solution_fields.html.erb (3.6ms)
Rendered people/_solution_fields.html.erb (3.5ms)
Rendered people/_solution_fields.html.erb (25.1ms)
Rendered people/_solution_fields.html.erb (3.5ms)
Rendered people/_solution_fields.html.erb (3.5ms)
Rendered people/_solution_fields.html.erb (25.7ms)
Rendered people/_solution_fields

trying to automate the opening of nested fields hangs the browser

I am sure I am doing something that I am not supposed to, but here is my set up:

  1. I have a one-to-many-to-many form which works correctly. The parent form is for scenarios. The next level many association is for well-schedules, and finally the next level down is rigs. Like so Scenario -> well-schedules -> rigs
  2. My goal is to set up the workflow such that when a) the user calls for a new Scenario form, it displays one record for well-schedules and another for rigs. and b) it similarly, it opens and displays one record for well-schedules and another for rigs when the user clicks on "Add well-schedules" link.
  3. 2.a, i.e., displaying both one well-schedules and another nested rigs record is easily accomplished using standard Rails code like so:

@Scenario = Scenario.new
1.times do
tw_schedule = @scenario.tw_schedules.build
1.times { tw_schedule.rigs.build }
end

Here tw_schedules represents (well-schedules).
4. It is the second part of step 2, i.e., 2.b, where things go wrong. I am using the insertion callback like so:

$(document).ready(function() {
...
$('#tw_schedules').bind('insertion-callback', function() {
showHideContractTypeWell();
$("a#add_rigs").last().trigger("click"); <---- this is the line of code that causes the browser to hang
});
....
});
5. Everything else in my jQuery code works fine except the offending line of code shown above. Using Firebug add-on for Firefox, I observed that the lowest level of the three entities, i.e., rigs link has an id called "add_rigs" and it is the same id for both initially added nested records for tw_schedules and rigs (as described in step 2.a above) and another record when I add a new record using "Add rigs field". -- a side note is I don't understand how can things work fine if both the Add rigs link have the same id but that is besides the point. So I am trying to fire the click event on the lower level 'Add rigs' link when a middle nested "tw_schedules" link is clicked.

What is it that I am doing wrong? Any help will be greatly appreciated.w

callback questions

I have some questions about callbacks in the cocoon.js. Everything here is in relation to the specific file from v1.0.15.

Question 1 - Why are the callbacks called on the parents of the add/remove links?

What about a situation, like mine, where the triggering add_association link does not live inside the html block that receives the new html? I have a toolbar containing my add link and the insertion node is in a separate container. The parent node of the add link, the toolbar, doesn't have, or need, a convenient id or class to build the selector that my jquery trigger-watching code can use. Was there a particular reason you didn't just trigger the callback off the add_link? thus line 50 goes from ...

$this.parent().trigger('insertion-callback');

to

$this.trigger('insertion-callback');

and you don't need a separate function for trigger_removal_callback. you can just use the following at lines 55 and 62.

$this.trigger('removal-callback');

much easier, imho. and easier for the developer to hang their trigger-watching functions off of.

Question 2 - Were lines 20 and 21 setting up insertionCallback and removalCallback variables forgotten about?

they aren't used but I could see you thinking of providing the developer another spot where you can configure something. I think it's easy enough to watch for the callbacks via jquery though.

(e)

Support for association conditions

This issue was first reported in #45. Essentially, conditions in ActiveRecord associations are not supported, see example (from #45):

class Post < ActiveRecord::Base
  has_many :comments
  has_many :admin_comments, class_name: "Comment", :conditions => { :author => "Admin" }
  has_many :people
end

Although the ticket was originally resolved, the fix was reverted in this commit:

37ab5dd#commitcomment-1792798

How to associate collection with another model as a requirement?

I have:

Exercise, LogEntry, and User.

Exercise has many :log_entries
User has many :log_entries

I was building app without users before, so I successfully created a nested form in Cocoon for Exercises to create Log Entry's.

Now all of the sudden a log entry belongs to a user so now I'm not sure how to make each log entry created through exercises to belong to a user.

I can't do [email protected]_attributes(params[:exercise])

In my head I can possibly iterate through the params and add in a user_id for each item in the array but that's lame. How should I be doing this properly? (Adding a hidden_field is lame too, so what's the right way?)

Here's my form for whatever it's worth but I don't think that matters since this is really a controller issue, right?:


      = semantic_form_for @exercise, :url => log_entries_path do |exercise|
        = exercise.semantic_fields_for :log_entries do |log_entry|
          = render 'log_entries/log_entry_fields', :f => log_entry
        .links
          = link_to_add_association 'Add Set', exercise, :log_entries, :partial => "log_entries/log_entry_fields", :data => {:role => "button", :icon => "plus"}
        = exercise.buttons do
          = exercise.commit_button "Apply", :button_html => {:data => {:icon => "check", :theme => "b"}}
          = link_to "Cancel", "", :data => {:rel => "back", :icon => "delete", :role => "button", :theme => "a"}

Edit: It almost seems like conditions => {:user_id => current_user.id} is what I want but that's not possible through the model. Thoughts?

Mouseover preview on link_to_add_association

I haven't read about this behavior but when mouse goes over a link_to_add_association link, there's a "preview" of the fields to be added that is displayed on the bottom of the page (my fields are in a "tr" supposed to be appended in a table).

Maybe this is because I'm using Bootstrap tooltip on this link (actually, that tooltip doesn't work anymore because of this problem).

Here's my link code :

link_to_add_association '', f, :filesystems, {rel: :tooltip, title: t_global(:add), class: ['icon-plus', 'icon-white'], data: {:'association-insertion-node' => '#node_filesystems', :'association-insertion-method' => :append}}

rel and title are the attributes used by bootstrap to display tooltips ; Bootstrap JS renames title to data-original-title.

use javascript not just clicks to add nested fields

Hi there, great gem you've got here! I was wondering if you have any plans to allow the use of javascript to programmatically inser the partial of fields besides the the "add ..." buttons. That way I can have more control over where the partial will eventually go.

Thanks!

can't acces apply a method on nested fields

.nested-fields
= f.inputs do
= f.object.mynestfield

is working

.nested-fields
= f.inputs do
= f.object.mynestfield.anymethod

is not working

do you know why ?

thank you very much

Disable_with on forms with cocoon

Hello
I use :disable_with on submit buttons of my forms, so user can't push the button twice:

<%= f.inputs do %>
<%= f.hidden_field :commentable_id %>
<%= f.hidden_field :commentable_type %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.input :comment_text, :input_html => { :class => 'wysiwyg'} %>
<% end %>

<div id="button">
<%= f.buttons do %>
    <%= f.commit_button :button_html => { :disable_with => "#{t('share.wait')}", :class => "wysiwyg_commit_button" } %>
<% end %>
</div>

But then I needed to add fields for attachments, like this:

<%=link_to_add_association t('asset.add'), f, :assets%> 

<%=form_tag :action => 'create' do %>
  <%= @assets.each_with_index do |asset, index| %>
    <%= fields_for "assets[#{index}]", asset do |f| %>
      <%= f.file_field :data  %>
      <%= f.text_field :description  %>
    <% end %>
  <% end %>
<% end %>

And....it broke, button is not disabling after push. Maybe I did anything wrong or there is another way to handle such forms?

Updated to Rails 3.1.1... nil.* error on Array?

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.*

This is within the "_model_entry_fields" partial.

= f.input :field, :label => false

Blows up on that.

I tested by reverting back to Rails 3.1.0 and no issue.

Anything I can test out to help?

has_many :through relationships?

We're trying to use cocoon to support the following relationship:

Product has_many :product_people
Product has_many :people, :through => :product_people
ProductPerson belongs_to :product
ProductPerson belongs_to :person
Person has_many :products, :through => :product_people

Now, the idea is that when creating a new product, we're trying to manage people, by adding more people and managing their titles.etc.

However, something appears to be not working her with cocoon. I can see that it's meant to look for something like _new_person or whatever, but instead of that we're seeing _0_person and I'm sure this is because of the partials and the arguments we're sending to cocoon helpers. However, I don't see a way around this. In the view we have:

= f.semantic_fields_for :product_people do |pp|
    = render 'person_fields', :f => pp
        %li
            Bio and details can be added after the film is accepted.
            .links
                = link_to_add_association 'Add person', pp, :person, :id => 'addRole'

I have tried changing the link_to_add_association by passing in "f" instead of "pp", but that just gives me ruby errors. How I see it - we appear to have done everything correctly, but this level of depth isn't supported?

Merge pull request #64 from semanticart/master

Hi,

There is a change here that breaks mongoid 2.4.9 with

ActionView::Template::Error (undefined method `collection?' for #<Mongoid::Relations::Metadata:0x000000057f46d0>):

Regards,

Build in i18n conventions

One of the nice things about simple_form is that it offers conventional locations for form i18n data. It'd be nice if cocoon did the same. While it's simple enough to add the calls to translate every time I use add or delete links, its unnecessarily tedious. Here's an idea that I kicked around for all of 10 minutes. Let me know what you think:

Define convention for i18n something like the following contrived example:

cocoon:
  add: "Add"
    user: "Add User"
    tag: "+"
  remove: "Remove"
    user: "Delete"
    tag: "-"

Calls to the add would perform a lookup to the proper node using the provided association name. Backwards compatibility would be a little tricky because arguments aren't passed as a hash, so it'd either be a breaking change or we'd have to check the number of arguments passed to determine if the i18n lookup should be done.

The remove helper doesn't explicitly take the association name, but it does tae the form builder object. We can get the class name from f.object.class.

If the specific class name translation isn't available it can fall back to the 'add' or 'remove' keys, then try to look in standard rails i18n locations to get a translation for the class name to append. If that's not available it can fall back to other sensible defaults.

Typing all this out makes it seem rather complicated, but I've really come to enjoy similar conventions offered by simple_form. The following:

= link_to_add_association t("action.add.#{Tag.model_name)"), f, :staffing_guidelines
= link_to_remove_association t('actions.delete'), f

... would become:

= link_to_add_association f, :staffing_guidelines
= link_to_remove_association f

What do you think? If it's something you think is worth pursuing, I might be able to get to it in the next couple of weeks. If you think it's too complicated or needs further fleshing out, that's fine too. I can implement this in my own project as a helper that wraps the current helpers if need be.

Thanks for the great gem!

Consider adding :allow_destroy => true to the read me

Hi,

Thank you for this great gem, worked like a charm, I am using rails 3.1 and mongoid.

The issue I had was with the remove part until I figured I have to add :allow_destroy => true to my model, it might help if you can mention this in the readme.

Thanks,

HTML ID duplication hurts new browsers

I noticed some strange behavior hitting checkboxes in my app on FF4 when adding multiple nested forms in the same go. I would click a checkbox, and a completely different one, or set of different ones, would invert state. Chrome Dev exhibited the same; Safari and older Firefox did not. I verified the behavior on the demo app.

My fork (https://github.com/rfc2616/cocoon) has a quick and dirty fix that demonstrates the issue is related to HTML ID attributes being reused, which Cocoon probably oughtn't to do for validity reasons anyway ... but it seems to make for actual bad behavior now.

I didn't request a pull of this hack on purpose, even though it Works For Me (TM) - it seems it could also replace things we don't want replaced or have other bad side effects. Still, I think there needs to be a way of ensuring that added elements have unique IDs. I have some time to work on a non-hacky solution if we can agree on a good approach.

I much prefer using Cocoon than hand-coding my own helpers to support these add/remove behaviors ... happy to beef it up.

trigger removal callback after the element is hidden?

Is there a reason the removal callback isn't called AFTER $this.closest(".nested-fields").hide();?

That was my assumption when I tried to use it to grab the visible .nested-fields for ordering.

Maybe have two separate callbacks. before-removal and after-removal?

sub form submission lost

I have a rails app using cocoon. I am using twitter bootstrap and have my screen split up. One side is a person and I have a content_for in my application file so the solutions will render on the other half of the screen. <%= yield(:solution_section) %> . When I submit the form it is not passing the solutions for data with the rest of the form. Is there a way to get this to work?

In the main person form I have this:

<%= content_for :solution_section do %>
<%= f.fields_for :solutions do |solution| %>
<%= render 'solution_fields', :f => solution %>
<%= link_to_add_association 'Add Solution', f, :solutions %>
<% end %>
<% end %>

Multiple nested partials from other forms

Hi. I'm using rails 3.2. Given I have the follow:

./views/article/_form.html.haml

= semantic_form_for @article do |f|
  = render 'contents/basic_data', :f => f

./views/contents/_basic_data.html.haml

%fieldset{:id => "dados_basicos"}
  #......................
  = f.semantic_fields_for :authors do |author|
    = render 'contents/author_fields', :f => author

./views/contents/_author_fields.html.haml

.nested-fields
  = f.inputs do
    #.......

When I access article/new, the following error appears:

Missing partial article/author_fields, inherited_resources/base/author_fields, application/author_fields

I.e., I think that these nested partial views are somehow messing with it's locations... because I sinalized that partial view _author_fields is at /views/contents/

To workaround this, I had to put _author_fields at /views/application/, but I'd like to know a better solution.

Thanks in advance! And congratulations for this project, by the way!

Partial counter

Is it possible to pass a counter value in the helper method? Or is it already available somewhere?

jQuery Mobile and AJAXily loading (add_fields) problem.

I had an interesting issue while using jQuery Mobile and I've debugged it to being a problem with loading the page "ajaxily."

Here's the issue:

Let's say you're browsing a list of tasks.

You click task 1 on that list.

(AJAX load /tasks/1)

Now you see the "cocoon" form to add todos to this task.

There's no todo's yet, so you click "add todo"

It adds a new set of fields but it doesn't fire up anything in the insertion-callback hook. I tested this by trying to output anything in the console and nothing would output.

If I then try the same thing on a page that already has a .nested-fields then it works.

Now I don't mean to confuse this because of it being a jQuery mobile problem. It could be my complete stupidity but something is odd here when it's loaded ajaxily. It very well could be my error and some caching issue with jQuery mobile but I'd appreciate any insight to help educate me!

I originally found this issue because inputs are styled by jQuery when a page initially loads. They're styled to make them look "native." So when you dynamically add new elements (cocoon) they weren't styled natively. They had the ugly generic html input box style. So then I created a .data("insertion-callback") to $(this).trigger("create"); which updates it to the jQuery mobile styling. This works... as an element from cocoon exists.

Issue with triple nested models with duplicate model names -- only last item of deepest model is submitted

Not sure what's going on here, having a very hard time isolating this bug, but it seems to come down to my cocoon forms (I'm also using Simple_Form).

Schema: Poll has many Questions has many Options has many Follow-ups (class: Question) has many Options
I have everything setup with cocoon, but when creating a poll with a follow-up with multiple options, only the last option is submitted to the controller. Editing works fine as long as the Question->Option are already saved to the db.

Seems to happen no matter what version of cocoon I use (1.0.22, 1.0.21, even 1.0.14 which is what I had been using initially).

Latest cutting edge branch: https://github.com/codeforamerica/textizen/tree/createpoll-prettify
Views: https://github.com/codeforamerica/textizen/tree/createpoll-prettify/app/views/polls

Update: My suspicion is that is has something to do with the deepest nested objects being assigned duplicate id's.

id = "poll_questions_attributes_1342830180740_options_attributes_1342830183683_follow_up_attributes_1342830185528_options_attributes_1342830183683_text"

id = "poll_questions_attributes_1342830180740_options_attributes_1342830183683_follow_up_attributes_1342830185528_options_attributes_1342830183683_text"

If the first option is already saved to the db, things save fine, it's only when creating option -> followup -> option all at once that things go screwy, I suspect because of the regex in cocoon.js that replaces new_assoc with the generated ID. We have two models with the same name in the hierarchy.

example of when it works fine:
poll_questions_attributes_0_options_attributes_0_follow_up_attributes_1342830185528_options_attributes_1342830183689_text

Examples in ERB?

Everyone doing Rails knows ERB- but not everyone knows SAAS, HAML, etc...

Any chance that the demos and documentation could be reworked in ERB for legibility for more people?

Update collection in _fields partial

Hello
I'm trying to implement dynamic changes in collection in select input of partial for nested object.
I'm using cocoon (1.0.15), formtastic (2.0.2), rails (3.1.3).
I have model Contact:

   class Contact < ActiveRecord::Base
     has_many :infos, :as => :informable
     accepts_nested_attributes_for :infos, :reject_if => proc {|attributes| attributes[:key].blank? || attributes[:value].blank? }, :allow_destroy => true
   end

I have model Info:

 class Info < ActiveRecord::Base
belongs_to :informable, :polymorphic => true
belongs_to :key
  end

And I have model Key:

  class Key < ActiveRecord::Base
has_many :infos
   end

Here is partial _info_fields.html.erb:

  <div class="nested-fields">
       <%= f.inputs do %>
       <%= f.input :key, :as => :select, :include_blank => false, :collection => Key.all %> 
         <%= f.input :value %>
        <%= link_to_remove_association "remove", f, :id => "remove_info" %>
      <% end %>
   </div>

And I have dialog, that can be called from the parent form directly and user can CRUD Key model from it. And I want collection in input :key on partial to update after these actions, but newly added partial contains old collection.
How can I get updated collection on newly added partials?

1.0.6 on jRuby fails.

Greetings.

When I try to install this version of cocoon under jRuby I get this error:

ERROR: While executing gem ... (ArgumentError)
undefined class/module YAML::Syck::DefaultKey

locking my Gemfile to an earlier version (1.0.5 works) works around this problem for me.

:validates_presence_of nested form's parent model

Sorry if I'm overlooking something but say I have an Organisation form with a nested Contact form.

Is it still possible to have validates_presence_of :organisation in the Contact model?

Right now I get form errors because the Organisation association must be defined on the Contact; but of course there is no Organisation record available until the Organisation has been created.

Thanks

undefined method `reflect_on_association' for NilClass:Class

I have the error below.
Do you have any ideas what I was doing wrong?

NoMethodError in User_searches#new

Showing /opt/rails/stkwebtest/app/views/user_searches/_form.html.haml where line #8 raised:

undefined method `reflect_on_association' for NilClass:Class
Extracted source (around line #8):

5: = f.simple_fields_for :details do |sf|
6: = render 'detail_fields', :f => sf
7: .links
8: = link_to_add_association 'add criteria detail', f, :details


$ cat user_search.rb

class UserSearch < ActiveRecord::Base
  has_many :details
  accepts_nested_attributes_for :details

  validates_presence_of :name

end

$ cat detail.rb

class Detail < ActiveRecord::Base
  belongs_to :user_search
end

_form.html.haml

= f.input :name, :label_html => {:class => 'text_field'}
  %h3 Criteria Details
  #search_fields
    = f.simple_fields_for :details do |sf|
      = render 'detail_fields', :f => sf
    .links
      = link_to_add_association 'add criteria detail', f, :details

_detail_fields.html.haml

.nested-fields
  = f.input :where_field
  = f.input :value
  = link_to_remove_association "remove search fields", f

Extend formbuilder?

Why not extend FormBuilder with the add/remove association methods? Then you could do:

f.link_to_add_association 'add task', :task

Seems a bit more natural this way.

Label method in inputs

I need to put some info of the nested model in the label of the input, but can't make it work

<%=f.input :puntuacion, :label_method => :to_label %>

if I use :label => :to_label, it just puts "To Label"
and the label method doesn't do anything.

What am I doing wrong?

Thanks!

How to add auto increment counter for rendered templates ?

Hello,

I would like to ask please, how can I do an auto increment counter that will be populated into a hidden field in the associated model template ?

My form has two models associated, with has_many relation for each model, and I need to give an incremental counter value for each added scope using link_to_add_association.

Why do I need the counter ? to be able to list the associated models in the same order added by the user, I've added a position field that will be a hidden in the template, is this a good idea ? or there is an easier way to do it ?

Can you please help me to achieve this goal ?

Thanks very much

fields_for not showing up, erb?

I implemented the code as you show in erb but I get everything else but the solution fields_for displaying, ideas?

<%= semantic_form_for(@person) do |f| %>
<%= render "shared/error_messages", :target => @person %>

<%= f.inputs do %>
<%= f.error_messages %>
<%= label_tag :person_date_of_problema, 'Date' %>
<%= datepicker_input :person, :date_of_problema %>
<%= f.input :name,:input_html => { :size => 10 } %>
<%= f.input :age, :as => :string, :input_html => { :size => 10 } %>
<%= f.input :weight, :as => :string, :input_html => { :size => 10 } %>
<%= f.input :height, :as => :select, :collection => (50..79).to_a.map{|n| [n.inches_to_feet,n]} %>
<%= f.input :conditions_tokens, :input_html => {'data-pre' => @person.conditions.map(&:attributes).to_json} %>

<%= f.semantic_fields_for :solutions do |solution| %>
  <%= render 'solution_fields', :f => solution %>
  <div class='links'>
    <%= link_to_add_association 'add solution', f, :solutions %>
  </div>
<% end %>

<%= f.buttons %>

<% end %>

<% end %>

Current records don't appear

Hello!
I've created a nested form using cocoon, that used to work at first (when it only had text_secions), but after adding some additional functionality it broke. Looking at the log it seems that it fetches the results from the database, but they don't show when editing the page. Here is some code:

_form

<ul class="nav nav-tabs" style="margin-left:160px" id="myTab">
  <li class="active"><a href="#post_basic">Статия</a></li>
  <li><a href="#post_advanced">Настройки</a></li>
  <li><a href="#post_gallery">Галерия</a></li>
</ul>
<%= simple_form_for([:manage, @document], :html => {:class => 'form-horizontal', multipart: true }) do |f| %>
    <%= f.error_notification %>
    <%= f.hidden_field :legacy, value: false %>
    <div class="tab-content">
        <div class="tab-pane active" id="post_basic">
            <%= f.input :title, label: "Заглавие", :input_html => {class: 100} %>
            <%= f.input :excerpt, label: "Кратко описание", as: :text, :input_html => {rows: 4, style: "width:350px"} %>

            <h3 style="margin-left:160px;margin-top:50px;">Секции</h3>
            <div id="text_section">
                <% f.fields_for :text_sections do |text_section| %>
                    <%= render 'text_section_fields', f: text_section %>
                <% end %>
                <div class="links"><%= link_to_add_association 'добави секция', f, :text_sections %></div>
            </div>
        </div>
        <div class="tab-pane" id="post_advanced">
            <%= f.input :slug, :input_html => {class: 100}, disabled: true %>
            <%= f.input :published, label: "Публикувана", value: true %>
            <%= f.input :featured_image, label: "Заглавна каритнка", as: :file %>
            <%= f.input :remote_featured_image_url, label: "или&nbsp;", as: :string, :placeholder => "линк към картинката" %>
            <%= f.association :categories, label: "Категории" %>
            <%= f.input :release_date, label: "Публикувай на" %>
            <%= f.input :pinged, label: "Слайдър" %>
            <%= f.input :fast, label: "Прочети набързо" %>
            <%= f.input :hot, label: "Горещо" %>
            <%= f.association :admin, :include_blank => false, :label_method => lambda { |admin| "#{admin.username}" } %>
        </div>
        <div class="tab-pane" id="post_gallery">
            <div id="gallery">
                <% f.simple_fields_for :gallery do |gallery| %>
                    <%= render 'gallery_fields', f: gallery %>
                <% end %>
                <div class="links"><%= link_to_add_association 'добави галерия', f, :gallery, :class => "add_gallery_" %></div>
            </div>
        </div>
    </div>
    <%= f.error :base %>    
    <%= f.button :submit, value: "Напред", class: 'controls btn-success' %>
<% end %>

_text_section_fields

<div class="nested-fields">
    <%= f.input :title, label: 'Заглавие на секцията' %>
    <%= f.input :body, label: 'Текст', input_html: {class: 'tinymce'} %>
    <%= tinymce %>
    <div class="links" style="margin-top:-20px">
        <%= link_to_remove_association "махни секцията", f %>
    </div>
</div>

_photo_fields

<h4 class="links">Снимка</h4>
<div class="nested-fields">
    <%= f.input :caption %>
    <%= f.input :description, as: :text, :input_html => {rows: 4, style: "width:350px"} %>
    <%= f.input :source, label: "Заглавна каритнка", as: :file %>

    <div class="links" style="margin-top:-20px;margin-left:160px">
        <%= link_to_remove_association "махни снимката", f %>
    </div>
</div>

_gallery_fields

<div class="nested-fields">
    <%= f.input :title, label: 'Заглавие на галерията' %>
    <div id="photo">
        <% f.simple_fields_for :photos do |photo| %>
            <%= render 'photo_fields', f: photo %>
        <% end %>
        <div class="links"><%= link_to_add_association 'добави снимка', f, :photos %></div>
    </div>
    <div class="links"><%= link_to_remove_association "махни галерията", f, class: 'remove_gallery_' %></div>

</div>

logs

Started GET "/manage/documents/5/edit" for 127.0.0.1 at 2012-09-18 05:30:42 +0300
Processing by Manage::DocumentsController#edit as HTML
  Parameters: {"id"=>"5"}
  Admin Load (0.5ms)  SELECT `admins`.* FROM `admins` WHERE `admins`.`id` = 1 LIMIT 1
  Document Load (0.2ms)  SELECT `documents`.* FROM `documents` WHERE `documents`.`id` = 5 ORDER BY documents.created_at DESC LIMIT 1
  TextSection Load (0.5ms)  SELECT `text_sections`.* FROM `text_sections` WHERE `text_sections`.`document_id` = 5
  Rendered manage/documents/_text_section_fields.html.erb (5.0ms)
  Rendered manage/documents/_text_section_fields.html.erb (4.9ms)
  Rendered manage/documents/_text_section_fields.html.erb (37.7ms)
  Rendered manage/documents/_text_section_fields.html.erb (5.5ms)
  Category Load (0.3ms)  SELECT `categories`.* FROM `categories` ORDER BY categories.position ASC
  Category Load (0.4ms)  SELECT `categories`.* FROM `categories` INNER JOIN `categorizations` ON `categories`.`id` = `categorizations`.`category_id` WHERE `categorizations`.`document_id` = 5 ORDER BY categories.position ASC
  Admin Load (0.6ms)  SELECT `admins`.* FROM `admins` 
  Gallery Load (0.5ms)  SELECT `galleries`.* FROM `galleries` WHERE `galleries`.`document_id` = 5 LIMIT 1
  Photo Load (0.3ms)  SELECT `photos`.* FROM `photos` WHERE `photos`.`gallery_id` = 3
  Rendered manage/documents/_photo_fields.html.erb (4.9ms)
  Rendered manage/documents/_gallery_fields.html.erb (8.8ms)
   (0.2ms)  BEGIN
   (0.3ms)  UPDATE `galleries` SET `document_id` = NULL, `updated_at` = '2012-09-18 02:30:42' WHERE `galleries`.`id` = 3
  SQL (0.2ms)  INSERT INTO `photos` (`caption`, `created_at`, `description`, `gallery_id`, `source`, `updated_at`) VALUES (NULL, '2012-09-18 02:30:42', NULL, 3, NULL, '2012-09-18 02:30:42')
   (37.1ms)  COMMIT
  Rendered manage/documents/_photo_fields.html.erb (5.0ms)
  Rendered manage/documents/_gallery_fields.html.erb (9.2ms)
  Rendered manage/documents/_form.html.erb (139.9ms)
  Rendered manage/documents/edit.html.erb within layouts/manage (140.3ms)
Completed 200 OK in 153ms (Views: 110.8ms | ActiveRecord: 41.0ms)
[2012-09-18 05:30:42] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true

Thanks in advance

Uncaught TypeError: Cannot call method 'replace' of null

I followed installation for 3.1 instructions and usage but link_to_add_association is not working.
I get error "Uncaught TypeError: Cannot call method 'replace' of null" when I click on the generated link.
I'm using rails 3.1 and formtastic.

Add fields to a relative node

Hi,

I'm using Cocoon with simple_form to manage 2 levels nested forms :

  • Node has_many volumes
  • Volume has_many filesystems

My form is composed of HTML tables and added fields are table rows.

When using link_to_add_association on node, to add volumes, I use the following, and new rows are successfully added :

data: {:'association-insertion-node' => '#node_volumes', :'association-insertion-method' => :append}

I only have one "#node_volumes" table so new fields are added to it.

But, when trying to add new filesystems to a volume, I can't just use a jQuery selector since id won't be unique, but I need to reach the parent of the parent of the link...

I could also use a selector, but inside a "$(this).closest([...])"

Is there a way to do this ?

Thanks !

Using cocoon in separate controller

I have Project model, projects controller and belonging them attachments.

Can I use cocoon for adding nested attributes in separate Attachments controller? For example projects/attachments/new

Thanks for help.

Add extra parameter for trigger insert or removal-callback

Hi,

I want to use your plugin in my application but I've got one problem. I need to use the node in trigger for other action (update some other data in relation with node).
I see in jquery api that trigger can take an extra parameter : trigger( eventType [, extraParameters] ). Perhaps it's possible to pass node argument in extra parameters. The code could be :

function trigger_removal_callback(node) {
node.parent().parent().trigger('removal-callback', node);
}

Nested form disappears with validation errors

If you have a form with a link_to_add_association link, the added nested form disappears when the form is submitted and validation errors occur.

I'm using this on the belongs_to end of the assocation (i.e. the parent model with the form belongs_to the nested model) and it doesn't matter whether the validation happens on the parent or nested form.

It should load the form again with the values in the params.

Problems with two levels nesting

Hello Nathan,
My project works fine with one level of nesting:

A scenario has many schedules

However, now my project requires that a schedule can have many rigs so it is a two levels nesting.

I am not having much luck with it.
Is that a limitation with Cocoon or is there something that I may be doing wrong?

Thanks in advance.

Bharat

Problems with fetching link data via jquery.data method

Hi Nathan!

I tried using your library today in combination with formtastic and I had a number of problems - all related to the data method. For some reason - whenever I queried manually for the a data attributes, they would always returned nil - which made your library almost useless. I updated the javascript so that it uses $.attr('data-whatever') instead and now it works fine.

If you like I can provide this update - which I don't particularly like as data should have worked, but it seems to be a problem?

Consistency in field naming

I was just tripped up by an issue where I had named my .nested-field section incorrectly - sure it was my mistake, but there is inconsistency:

  • link for adding fields uses .add_field
  • link for removing fields uses .remove_field
  • section for form uses .add-field

!!!

Cocoon for Array fields

I have a nested form related to an Array field.

Everything looks like if it was an association except that...it's not an association. Just an Array because each record is a single string value and creating a new model & DB table for just one text field is a little too much.

Everything work fine with the form but when trying to add new rows with link_to_add_association, I get this :

undefined method `collection?' for nil:NilClass

/usr/local/rvm/gems/ruby-1.9.3-p194/bundler/gems/cocoon-c7eb5b214df7/lib/cocoon/view_helpers.rb:94:in `create_object'
/usr/local/rvm/gems/ruby-1.9.3-p194/bundler/gems/cocoon-c7eb5b214df7/lib/cocoon/view_helpers.rb:75:in `link_to_add_association'

Pure logic : it's not an association.

My question is the following : Is there a way to use cocoon on nested Array forms ?

I know cocoon is based on associations, but the feature would be fine with Array fields too.

I'll look at the code to find a solution (maybe a new link_to_add_array_field method, that would't need to create a child object...), but if there's a built-in solution, i'll take it ;o)

Couldn't find wrapper with name inline

I'm trying to model your examples using simple_form and Twitter Bootstrap, but I just can't seem to get them working. Getting an error about wrapper with the name inline. Where do I define this wrapper with the name inline?

<%= simple_form_for @broadcast, :wrapper => 'inline' do |f| %>
  <%= f.input :name %>
  <%= f.simple_fields_for :product_images, :wrapper => 'inline' do |image| %>
<%= render :partial => 'product_image_fields', :f => image %>
  <% end %>
    <%= f.submit nil, :class => 'btn btn-primary' %>
<% end %>

bug with has_many association with different :class_name value?

Hi Nathan. Love this gem since it saved me so much time in my current project, however I think I've found a bug. I am outlining it here first since I don't know if/when I'll have a chance to fork and build tests for my patch.

I have a has_many association like below where the association has a different name than the class it's associating to.

class Asset < ActiveRecord::Base
  has_many :translations, :dependent => :destroy, :class_name => 'AssetTranslation'
  accepts_nested_attributes_for :translations, :reject_if => :insufficient_translation_data_submitted, :allow_destroy => true
end

class AssetTranslation < ActiveRecord::Base
  belongs_to :asset
end

here's how I'm calling link_to_add_association

<%= link_to_add_association 'Add translation', f, :translations %>

I'm using formtastic and using cocoon v1.0.2 I was seeing a javascript bug. it was saying "content is null" for cocoon line 10. the bug is in the view_helpers.rb file. you are building the "data-association" value based on what's passed into "link_to_add_association", instead of reflecting on the association and building it from the class name.

so before my update it was searching for "new_translation", when the actual value it should be replacing is "new_asset_translation".

The gist at https://gist.github.com/869849 shows my new view_helpers.rb file with what worked for me. Note reordering and tweaks of lines 60-64 in original into lines 63-67 in mine, and the addition of reflecting again at the start of render_association. I know it's not DRY to do it this way but due to the time constraint on my project I didn't have time to go back and optimize.

I hope to be able to fork and fix with tests but I'm a little strapped for free time right now. I did want you to see this though, Nathan, to see if you had any comments.

silent (e)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.