Rails 4.2,AJAX表单在每次后续触发时以指数方式提交给服务器

Rails 4.2, AJAX form is submitted exponentially to the server on each subsequent triggering

我在表格(公司 select)上有一个 select,更改选项 selected 将更新另一个 select 中的可能选项(地址 select).通过提交 AJAX 请求更新地址 select 的选项。地址select的选项每次都更新成功

但是,当第一家公司被 selected 时,AJAX get 请求发送了 1 次。当第二家公司被 selected 时,AJAX 请求被发送 2 次。然后是4,然后是8,然后是16,然后是32等等

不理想...

最后一点,我一直在尝试遵循 Brandon Hilkert 关于 organizing javascript.

的建议

app/views/layouts/application.html.erb

<!DOCTYPE html>
  <html lang="en">
    <head>
    <%# Disables turbolinks caching of pages, to allow smooth animations %>
    <meta name="turbolinks-cache-control" content="no-preview">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
    <%= csrf_meta_tags %>
    <%= favicon_link_tag 'logos_and_banners/ecl_logo.png' %>
    <title>Environmental Concern Ltd - Your future in our hands.</title>

    <%# Geocompete JQuery plugin  %>
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=AIzaSyDfo9YlYY6BnemhdpDMlQbo6li3RyRYO-0&libraries=places"></script>
    <%= render partial: 'shared/flash' %>
    <%= render 'shared/navigation/navigation' %>
    <%# console %>
  </html>

app/views/licenses/_form.html.erb

<%= simple_form_for([@company, @license], remote: true, html: {role: 'form'}) do |f| %>
  <%# errors will be loaded here via AJAX  %>
  <div id="form-errors" class="alert alert-danger collapse"></div>

  <form role="form">
    <div class="form-inputs">
      <%= f.association :company,
            collection: Company.sorted_by('company_name_asc'),
            label_method: :company_name,
            value_method: :id,
            include_blank: false %>
      <%= f.association :address,
            collection: @company.addresses.sorted_by('site_name_asc'),
            label_method: :site_name,
            value_method: :id,
            include_blank: true %>
      <span class="help-block">
        Certain License types are related to a specific Address, others are only linked to the Company.
      </span>
      <%= f.input :license_type,
            collection: License::ALL_VALID_LICENSE_TYPES %>
      <%= f.input :license_name, as: :hidden%>
      <%= f.input :license_no %>
      <%= f.input :expiry_date,
            as: :string,
            input_html: {
              data: {
                provide: 'datepicker', date_format: 'yyyy-mm-dd'
              }
            }%>
    </div>
    <%= render partial: 'shared/edit_new/save_button', locals: {model_name: find_model_name_for_view(@license)} %>
  </form>
<% end %>

app/assets/javascripts/application.js

//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require bootstrap-sprockets
//= require cocoon
//= require turbolinks
//= require data-confirm-modal
//= require jquery.geocomplete
//= require bootstrap-datepicker/core
//= require underscore
//= require gmaps/google
//
//= require init
//= require licenses/app.form.js

app/assets/javascripts/init.js

window.App || (window.App = {});

// Define init function on the App object
App.init = function(){

  // Bootstrap tooltips. Must be manually enabled.
  $(function () {
    $('[data-tooltip="true"]').tooltip();
  })
};

// Call the init function on every page transition.
// $(document).ready functions don't fire with turbolinks
$(document).on("turbolinks:load", function() {
  return App.init();
});

app/assets/javascripts/licenses/app.form.js

App.license = (function() {
  function licensesFormBehaviour() {

    //Update address select based on Company choice
    $('select#license_company_id').change(function() {
      return $.ajax({
        url: 'addresses',
        type: 'GET',
        dataType: 'script',
        data: {
          company_id: $("select#license_company_id option:selected").val()
        },
        error: function(jqXHR, textStatus, errorThrown) {
          return console.log("AJAX Error: " + textStatus);
        }
      });
    });

  // Several other unrelated methods, which function fine.
  // I've omitted them to make the code block shorter.

  return licensesFormBehaviour;
})();

$(document).on("ajaxSuccess", function() {
  //Licenses form only
  if (!($("form.edit_license").length > 0) && !($("form.new_license").length > 0) ) {
    return;
  }
  //invoke
  var license = new App.license();
  return license;
});

谢谢你的时间, 帕特里克

编辑:

响应,芬达要求。

AJAX 回应

// Within the append() I've omitted around 30 additional <option> tags, for readability. 
$("#license_address_id").empty().append("<option value=\"\"></option><option value=\"197\">Berge Knolls<\/option>");

您提到您通过 ajax 加载表单,因此在 ajaxSuccess 上初始化表单,但正如 fanta 在评论中提到的那样,也在 [=41] 之后初始化许可证=] 请求完成,导致出现多个实例。

要解决这个问题,您可以将 licence 实例存储在数据 属性 中,并且仅在它不存在于表单元素中时才初始化:

$(document).on("ajaxSuccess", function() {
  $("form.edit_license, form.new_license").each(function(i, form) {
    var $form = $(form);
    var license = $form.data('license');
    if (!license) $form.data('license', new App.license());
  })
});

奖励内容:Rails(或者说 jquery-ujs)已经免费为您提供了 select/AJAX 行为! :)

如果将 data-remotedata-urldata-method 添加到 select 元素,jquery-ujs 将序列化元素的值对你的要求。在你的情况下,使用 simple_form,这有点棘手,但如果文档是正确的,那么你可以做类似的事情:

<%= f.input :company do %>
  <%= f.select
    :company,
    options_from_collection_for_select(
      Company.sorted_by('company_name_asc'), 'id', 'company_name'
    ),
    { include_blank: false },
    data: {
      remote: true,
      method: 'get',
      url: addresses_path
    }
  %>
<% end %>

当 select 更改时,jquery-ujs 会将 GET 更改为 /addresses?company_id=:selected_id,您的响应可以接管。

希望对您有所帮助!