Rails 6 form_with远程 UJS,绑定额外的处理程序来选择或提交

Rails 6 form_with remote UJS, bind extra handler to select or submit

提问人:nimmolo 提问时间:2/14/2023 最后编辑:Ryan Mnimmolo 更新时间:2/18/2023 访问量:139

问:

在我的 Rails 应用程序中,我有一个用于对各种“帖子”标题进行投票的功能。该表单有一个元素,并远程提交到 using .我想以正常方式处理响应,在接收请求的控制器中,使用 .formselectVotesControllerrails-ujsrespond_to do |format|

一种选择是添加到构建器中,正如其他答案所建议的那样。{:onchange=>'$(this.form).submit();'}f.select

但是,由于表单提交大约需要 8 秒,因此我想将我自己的处理程序添加到 或 (哪个事件对我来说无关紧要)selectonchangeformonsubmit

  • 禁用同一页面上的所有其他表单
  • 显示一个模态,说明我们为什么要禁用其他表单
  • 每个用户只允许一次“收藏”投票(降级之前对其他游戏的投票)
  • 在 Select 中存储一些数据属性以供以后处理

我遇到的问题是

  • 如果我将我的额外处理程序绑定到 select 事件,我可以通过 native 触发提交,但浏览器会询问我是否要离开当前页面。我可以通过将我自己的AJAX调用添加到我的处理程序来解决此问题,但我正在尝试使用默认的Rails控制器并处理提交。我只想添加一个额外的处理程序来在缓慢的响应到达之前更改 DOM。changerails-ujsrails-ujs
  • 如果我将额外的处理程序绑定到表单事件,则表单不会提交,并且绑定对 DOM 中的元素没有任何影响,尽管没有控制台错误。submit

这是我的代码:

# partial appears on PostsController#show
# there are several of these on the page, for voting on post titles

<%= form_with(model: @vote, url: naming_vote_path(naming_id: naming.id),
              method: :patch, local: false, id: "cast_vote_#{naming.id}",
              class: "naming-vote-form") \
              do |f| %>
  <%= f.select(:value, menu, {},
               { class: "form-control w-100",
                 # onchange: "this.form.submit();", # works but does not allow further bindings
                 data: { role: "change_vote", id: naming.id } }) %>
<% end %>

  $(document).ready(function () {

    var change_vote_selects = function () {
      return $("[data-role='change_vote']");
    };

    var attach_bindings = function () {
      change_vote_selects().on("change", function (event) {
        var _this = $(this);
        var value = _this.val();
        var naming_id = _this.data("id");
        _haveVotesChanged = true;

        // If setting vote to 3.0, go through all the rest and downgrade any
        // old 3.0's to 2.0.  Only one 3.0 vote is allowed. Also disable all
        // the selects while the AJAX request is pending.
        if (value == "3.0") {
          change_vote_selects().each(function () {
            var _this2 = $(this);
            if (_this2.data("id") != naming_id && _this2.val() == "3.0") {
              _this2.val("2.0");
            }
            _this2.attr("disabled", "disabled");
          });
        }

        // modal printed in layout already, add text to it and activate
        $('#naming_ajax_progress_caption').empty().append(
          $("<span>").text(translations.show_namings_saving + "... "),
          $("<span class='spinner-right mx-2'></span>")
        );
        $("#naming_ajax_progress").modal('show');

        _this.parent().submit();
      });

      // Save initial value in case of error, when we'll need to revert.
      change_vote_selects().each(function (event) {
        var _this = $(this);
        _this.data("old_value", _this.val());
        _this.attr("disabled", null);
      });
    };

    // Alert the user if they haven't saved data.
    window.onbeforeunload = function () {
      if (_haveVotesChanged && !_haveVotesBeenSaved)
        return translations.show_namings_lose_changes;
    }

    attach_bindings();
  });
javascript jquery ajax ruby-on-rails-6.1

评论


答:

0赞 nimmolo 2/14/2023 #1

多亏了这个答案,我需要在表单上触发一个原生的 rails-ujs 提交,以远程提交。

  $(document).ready(function () {

    var change_vote_selects = function () {
      return $("[data-role='change_vote']");
    };

    var attach_bindings = function () {
      change_vote_selects().on("change", function (event) {
        var _this = $(this);
        var value = _this.val();
        var naming_id = _this.data("id");
        _haveVotesChanged = true;

        // If setting vote to 3.0, go through all the rest and downgrade any
        // old 3.0's to 2.0.  Only one 3.0 vote is allowed. Also disable all
        // the selects while the AJAX request is pending.
        if (value == "3.0") {
          change_vote_selects().each(function () {
            var _this2 = $(this);
            if (_this2.data("id") != naming_id && _this2.val() == "3.0") {
              _this2.val("2.0");
            }
            _this2.attr("disabled", "disabled");
          });
        }

        // modal printed in layout already, add text to it and activate
        $('#naming_ajax_progress_caption').empty().append(
          $("<span>").text(translations.show_namings_saving + "... "),
          $("<span class='spinner-right mx-2'></span>")
        );
        $("#naming_ajax_progress").modal('show');

        nativeFormEl = _this.parent()[0]
        Rails.fire(nativeFormEl, 'submit')

      });

      // Save initial value in case of error, when we'll need to revert.
      change_vote_selects().each(function (event) {
        var _this = $(this);
        _this.data("old_value", _this.val());
        _this.attr("disabled", null);
      });
    };

    // Alert the user if they haven't saved data.
    window.onbeforeunload = function () {
      if (_haveVotesChanged && !_haveVotesBeenSaved)
        return translations.show_namings_lose_changes;
    }

    attach_bindings();
  });