Rails - 同步标签

时间:2018-02-03 20:53:34

标签: javascript jquery ruby-on-rails ruby forms

我有一个带bootstrap-tabs的视图。选项卡是动态生成的。

<%= form_with(:id => 'my-form', model: [:admin, @island], local: true) do |form| %>
  <div class="tab-content bg-light" id="tabs-from-locales-content">

    <%= available_locales.each_with_index do |locale, i| %>
      <div
      class="tab-pane fade <%= 'show active' if i == 0 %>"
      id="<%= locale.downcase %>"
      role="tabpanel"
      aria-labelledby="<%= locale.downcase %>-tab">
        <%= render partial: 'island_form', locals: {counter: i, locale: locale, f: form} %>
      </div>
    <% end %>

  </div>

...
...

标签表示应用的每个可用本地化。 表单的模型包含两个嵌套属性。这些属性与模型有1 to many的关系。因此,用户可以从表单中添加多个。他们的字段可以动态生成:

(为简单起见,我只在问题中包含一个。这是_island_form.html.erb部分的一部分。)

<div class="form-group ports-div">
  <%= f.label :port %> </br>
  <%= f.fields_for :ports do |builder| %>

    <%= render 'port_fields', f: builder %>

  <% end %>
  <%= link_to_add_fields t('form.add_port'), f, :ports %>
</div>

<div class="form-group">
  <%= f.label :airport %> </br>
  <%= f.fields_for :airports do |builder| %>

    <%= render 'airport_fields', f: builder %>

  <% end %>
  <%= link_to_add_fields t('form.add_airport'), f, :airports %>
</div>

port_fields部分:

<fieldset>
  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>
  <%= f.hidden_field :_destroy %>
  <%= link_to t('form.remove'), '#', class: 'remove_fields' %>
</fieldset>

link_to_add_fields辅助方法:

  def link_to_add_fields(name, f, association)
    # Builds an instance of the association record.
    new_object = f.object.send(association).klass.new
    # Grabbing ruby's object id.
    id = new_object.object_id
    fields = f.fields_for(association, new_object, child_index: id) do |builder|
      render(association.to_s.singularize + '_fields', f: builder)
    end
    link_to(name, '#', class: 'add_fields', data: { id: id, fields: fields.gsub('\n', '') })
  end

我想要实现的是在可用选项卡之间同步这些字段的添加和删除。因此,当用户点击第一个标签上的Add field时,所有其他标签都会执行此操作。对于字段删除也是如此。

js按钮的相关Add文件如下所示。我在triggertriggerHandlerstopPropagation之间尝试了很多组合,但大多数时候我都会StackOverflow exception,并且这些字段只会添加到我点击的标签中add按钮。

(因为我在.add_fields中传递了一个类(selector),该类应该附加到类.add_fields的所有元素上?)

form.on('click', '.add_fields', function (event, param) {
    event.stopPropagation();
    event.preventDefault();

    var time = new Date().getTime();
    var regexp = new RegExp($(this).data('id'), 'g');
    $(this).before($(this).data('fields').replace(regexp, time));

    $('.tab-pane').each(function (index) {

        $('.add_fields').trigger("click", ["custom_click"]);
        console.log('Tab - ' + index);
    })
});

修改

我正在使用该代码:

    $('.ports-div').each(function (index) {

        $(this).find('.add_fields').each(function () {
            this.click();
        })
    });

尽管在所有选项卡中都添加了1个字段6字段。我将ports-div添加到包含所有相关元素的div中。

1 个答案:

答案 0 :(得分:1)

完全重写......

您所看到的问题是,您的单击事件处理程序触发了对所有选项卡窗格的单击,包括您当前正在处理的选项卡窗格。您不仅可以点击循环中的单个项目,还可以点击所有匹配的“.add_fields”#。然后,这会导致再次以递归方式处理点击。

为了防止这种情况,我建议调用一个函数来直接添加字段,而不是触发点击。如果在您的情况下触发单击操作最简单,请考虑以下示例,该示例大致执行您想要的操作而不会出现递归错误。

https://jsfiddle.net/3ftf0j8e/1/

虚拟HTML

<a class="add_fields" id='1'>link 1</a>    
<a class="add_fields" id='2'>link 2</a>    
<a class="add_fields" id='3'>link 3</a>    
<a class="add_fields" id='4'>link 4</a>

示例Javascript

$(document).on('click', '.add_fields', function (event, param) {
    event.preventDefault();


    var clicked_id = $(this).attr('id');
    console.log('clicked id:' + clicked_id);

    $(this).addClass('done');
    $(this).addClass('clicked');
    // Just click items that have not been clicked
    var els = $('.add_fields').not('.clicked');
    console.log(els);    
    els.trigger("click");

    setTimeout(function(){
      if($('.add_fields').length == $('.add_fields.done').length)
        $('.add_fields').removeClass('clicked');
    })

});

<强> CSS

a.clicked {
  background-color: yellow;
}

a.done {
  color: red;
}

正如您现在所看到的,每次只发射一次。最后的setTimeout允许在清除单击的类之前更新DOM。