在预构建嵌套模型实例时,Rails nested_form link_to_add不起作用

时间:2012-04-19 14:38:57

标签: ruby-on-rails jquery-plugins ruby-on-rails-3.1 partial-views nested-attributes

我正在粗略地将ryanb的令人敬畏的nested_form gem集成到我的rails 3.1.3应用程序中。我担心我的Javascript技能太有限,无法知道它是我的代码(可能)还是需要更改的宝石。也许这里有人可以提供帮助。

设置:我有一个“:household”类:accepts_nested_attributes_for“:members(people)”。我正在运行开发服务器。我将nested_form.js移动到/ app / assets / javascripts目录。我几乎肯定它只被采购一次。

问题:如果在家庭控制器“新”方法中,我这样做:

@household = Household.new

我只在视图中看到家庭原生字段(预期),“link_to_remove”和“link_to_add”链接呈现/删除成员字段部分(预期)。但是,如果我这样做:

@household = Household.new
@household.members.build

我在视图中看到了家庭原生字段(预期),成员本地字段的一个呈现部分(预期),但“link_to_remove”和“link_to_add”什么都不做(意外)。我不能添加另一个:成员部分在那一点,也不删除已经显示的:members partial。

我很难过。下面是看似相关的精简源文件。我从git存储库获取了nested_form插件(最后捆绑了2012.04.18)......


/app/models/household.rb

class Household < ActiveRecord::Base
  has_many :members, :class_name => "Person"
  accepts_nested_attributes_for :members
  attr_accessible :id, :name, :member_ids
  attr_accessible :members_attributes
end #class

/app/models/person.rb

class Person < ActiveRecord::Base
  belongs_to :household  
  attr_accessible :id, :name_given, :name_middle, :name_family, :household_id 
end #class

/app/controllers/households_controller.rb

  <snip>
  # GET /households/new
  # GET /households/new.json
  def new
    @household = Household.new
    @household.members.build     # <---- Removing this changes the behavior

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @household }
    end
  end

/app/views/households/new.html.haml

.headbg
  .pad
    %h2 Enter a New Household
= render 'form'

/app/views/households/_form.html.haml

= nested_form_for @household, :html => { :class => "form-horizontal"} do |f|

  %fieldset 
    %legend Household

    .control-group
      = f.label( :name, { :class => 'control-label'} )
      .controls
        = f.text_field( :name, { :class => 'span5', :placeholder => '[household name]'} )

  %fieldset 
    %legend Household Members
    = f.fields_for :members, :html => { :class => "form-inline"} do |nested_f|
      = render :partial => 'people/nested_person_form', :locals => { :f => nested_f }
      = yield :nested_person_form
    %p
      = f.link_to_add "New Household Member", :members 

  .form-actions
    = button_tag( :class => "btn btn-primary", :disable_with => "Saving..."  ) do
      %i.icon-ok.icon-white
      Save
    = link_to households_path do
      .btn.btn-info
        %i.icon-arrow-left.icon-white
        Back to Households

/app/views/people/_nested_person_form.html.haml

- content_for :nested_person_form do

  .nested-fields
    .row
      .span8 
        .control-group
          = f.label( "Name", { :class => 'control-label'} )
          .controls
            = f.text_field( :name_given, { :class => 'span2', :placeholder => '[first]'} )
            = f.text_field( :name_middle, { :class => 'span2', :placeholder => '[middle]'} )
            = f.text_field( :name_family, { :class => 'span2', :placeholder => '[last]'} ) 
      .span1
        = f.link_to_remove "Remove"

/app/assets/javascripts/nested_form/nested_form.js

jQuery(function($) {
  window.NestedFormEvents = function() {
    this.addFields = $.proxy(this.addFields, this);
    this.removeFields = $.proxy(this.removeFields, this);
  };

  NestedFormEvents.prototype = {
    addFields: function(e) {
      // Setup
      var link    = e.currentTarget;
      var assoc   = $(link).attr('data-association');            // Name of child
      var content = $('#' + assoc + '_fields_blueprint').html(); // Fields template

      // Make the context correct by replacing new_<parents> with the generated ID
      // of each of the parent objects
      var context = ($(link).closest('.fields').find('input:first').attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');

      // context will be something like this for a brand new form:
      // project[tasks_attributes][new_1255929127459][assignments_attributes][new_1255929128105]
      // or for an edit form:
      // project[tasks_attributes][0][assignments_attributes][1]
      if (context) {
        var parentNames = context.match(/[a-z_]+_attributes/g) || [];
        var parentIds   = context.match(/(new_)?[0-9]+/g) || [];

        for(var i = 0; i < parentNames.length; i++) {
          if(parentIds[i]) {
            content = content.replace(
              new RegExp('(_' + parentNames[i] + ')_.+?_', 'g'),
              '$1_' + parentIds[i] + '_');

            content = content.replace(
              new RegExp('(\\[' + parentNames[i] + '\\])\\[.+?\\]', 'g'),
              '$1[' + parentIds[i] + ']');
          }
        }
      }

      // Make a unique ID for the new child
      var regexp  = new RegExp('new_' + assoc, 'g');
      var new_id  = new Date().getTime();
      content     = content.replace(regexp, "new_" + new_id);

      var field = this.insertFields(content, assoc, link);
      $(link).closest("form")
        .trigger({ type: 'nested:fieldAdded', field: field })
        .trigger({ type: 'nested:fieldAdded:' + assoc, field: field });
      return false;
    },
    insertFields: function(content, assoc, link) {
      return $(content).insertBefore(link);
    },
    removeFields: function(e) {
      var link = e.currentTarget;
      var hiddenField = $(link).prev('input[type=hidden]');
      hiddenField.val('1');
      // if (hiddenField) {
      //   $(link).v
      //   hiddenField.value = '1';
      // }
      var field = $(link).closest('.fields');
      field.hide();
      $(link).closest("form").trigger({ type: 'nested:fieldRemoved', field: field });
      return false;
    }
  };

  window.nestedFormEvents = new NestedFormEvents();
  $('form a.add_nested_fields').live('click', nestedFormEvents.addFields);
  $('form a.remove_nested_fields').live('click', nestedFormEvents.removeFields);
});

1 个答案:

答案 0 :(得分:6)

我遇到了同样的问题,并通过以下方式修复:

确保你头脑中有一个最新的jQuery.js文件。加载后,请不要使用从gem安装的nested_form.js文件。而是使用jquery_nested_form.js。

只要在jquery_nested_form文件之前有jquery链接,这个解决方案就可以正常工作:

    <%= javascript_include_tag :defaults, 'jquery_nested_form' %>

以下是jquery_nested_form.js文件的代码:

    jQuery(function($) {
  window.NestedFormEvents = function() {
    this.addFields = $.proxy(this.addFields, this);
    this.removeFields = $.proxy(this.removeFields, this);
  };

  NestedFormEvents.prototype = {
    addFields: function(e) {
      // Setup
      var link    = e.currentTarget;
      var assoc   = $(link).attr('data-association');            // Name of child
      var content = $('#' + assoc + '_fields_blueprint').html(); // Fields template

      // Make the context correct by replacing new_<parents> with the generated ID
      // of each of the parent objects
      var context = ($(link).closest('.fields').closestChild('input, textarea').eq(0).attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');

      // context will be something like this for a brand new form:
      // project[tasks_attributes][new_1255929127459][assignments_attributes][new_1255929128105]
      // or for an edit form:
      // project[tasks_attributes][0][assignments_attributes][1]
      if (context) {
        var parentNames = context.match(/[a-z_]+_attributes/g) || [];
        var parentIds   = context.match(/(new_)?[0-9]+/g) || [];

        for(var i = 0; i < parentNames.length; i++) {
          if(parentIds[i]) {
            content = content.replace(
              new RegExp('(_' + parentNames[i] + ')_.+?_', 'g'),
              '$1_' + parentIds[i] + '_');

            content = content.replace(
              new RegExp('(\\[' + parentNames[i] + '\\])\\[.+?\\]', 'g'),
              '$1[' + parentIds[i] + ']');
          }
        }
      }

      // Make a unique ID for the new child
      var regexp  = new RegExp('new_' + assoc, 'g');
      var new_id  = new Date().getTime();
      content     = content.replace(regexp, "new_" + new_id);

      var field = this.insertFields(content, assoc, link);
      // bubble up event upto document (through form)
      field
        .trigger({ type: 'nested:fieldAdded', field: field })
        .trigger({ type: 'nested:fieldAdded:' + assoc, field: field });
      return false;
    },
    insertFields: function(content, assoc, link) {
      return $(content).insertBefore(link);
    },
    removeFields: function(e) {
      var $link = $(e.currentTarget),
          assoc = $link.data('association'); // Name of child to be removed

      var hiddenField = $link.prev('input[type=hidden]');
      hiddenField.val('1');

      var field = $link.closest('.fields');
      field.hide();

      field
        .trigger({ type: 'nested:fieldRemoved', field: field })
        .trigger({ type: 'nested:fieldRemoved:' + assoc, field: field });
      return false;
    }
  };

  window.nestedFormEvents = new NestedFormEvents();
  $('form a.add_nested_fields').live('click', nestedFormEvents.addFields);
  $('form a.remove_nested_fields').live('click', nestedFormEvents.removeFields);
});
// http://plugins.jquery.com/project/closestChild
/*
 * Copyright 2011, Tobias Lindig
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 */
(function($) {
        $.fn.closestChild = function(selector) {
                // breadth first search for the first matched node
                if (selector && selector != '') {
                        var queue = [];
                        queue.push(this);
                        while(queue.length > 0) {
                                var node = queue.shift();
                                var children = node.children();
                                for(var i = 0; i < children.length; ++i) {
                                        var child = $(children[i]);
                                        if (child.is(selector)) {
                                                return child; //well, we found one
                                        }
                                        queue.push(child);
                                }
                        }
                }
                return $();//nothing found
        };
})(jQuery);

希望这有帮助!