升级到 rails 5.2 - 远程表单现在刷新页面

Posted

技术标签:

【中文标题】升级到 rails 5.2 - 远程表单现在刷新页面【英文标题】:Upgrading to rails 5.2 - remote forms now refreshing the page 【发布时间】:2021-06-06 14:19:19 【问题描述】:

我正在尝试将 Rails 5.1.7 应用程序升级到 5.2.0。网站上的一些表格因升级而损坏。表单的模板如下所示:

<%= form_with model: record, url: client_field_value_path(@dataset, record), remote: true do |form| %>
  <%= the form contents %>
<% end %>

我认为表单中唯一相关的部分是它使用了remote: true。我发现在 Rails 5.2 中 remote: true 已更改为 form_with 助手的默认值。我相信这是用不显眼的javascript实现的,但我不太了解ujs。

该表单旨在在不重新加载页面的情况下提交,并且页面中的状态更改由 ajax 侦听器处理。我们使用 jquery - 客户端代码包括以下行:

  $(this.form).trigger('submit');

现在正在执行本地提交,而不是远程提交。因此页面刷新,JSON 响应被渲染。我发现an issue raised against rails-ujs 看起来很相关,他们建议将$(form).submit() 替换为

form = document.querySelector('form');
form.dispatchEvent(new Event('submit', bubbles: true));

或者,使用 rails-ujs,

form = document.querySelector('form');
Rails.fire(form, 'submit');

我已将rails-ujs 添加到我的application.js 文件中:

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
// ....
//= require jquery
//= require jquery_ujs
//= require rails-ujs
// ....

(jquery 和 jquery_ujs 已经存在)。我尝试了上述两个建议,但似乎都没有触发表单的提交。我真的被困住了!如果我可以提供更多详细信息,请询问。我不太了解ujs的情况,所以有点迷茫。

编辑:完整的保存调用 - 当单击“保存”按钮时触发(但该按钮不是 &lt;input type=submit&gt;,它只是一个样式按钮)。

  save(event) 
    if( event ) 
      event.preventDefault();
      event.stopPropagation();
    

    this.container.addClass('saving');

    // this.form.submit();
    Rails.fire(this.form[0], 'submit');


    this.container.find(".field-controls button").prop('disabled', true);
  

更新:我已经设法让它工作了。我真的很感激能帮助我了解以下更改的来源,以及我的应用程序配置中是否存在某些问题,这使得它变得比必要的更难。

在表单中将remote: true 显式更改为local: false。 没有这个,data-remote=true 属性不会在 html 中设置。 将其留空是不够的,尽管它应该是默认设置。如果我不提供local: false,我会进入控制台:
$('form[data-remote=true]').length
0
根据this answer,默认值由Rails.application.config.action_view.form_with_generates_remote_forms配置。在控制台中,这个值是 nil。我找不到它被覆盖的任何地方。

在我的 JS 资产中添加了 rails-ujs 并删除了 jquery_ujs

我需要包含rails-ujs 才能定义Rails(下一步需要)。 当包含rails-ujs 时,我收到了一条JS 警告,指出应该删除jquery_ujs。我认为这包含在 jquery-rails gem 中 - 我也可以安全地删除它吗?

this.form.submit() 更改为Rails.fire(this.form[0], 'submit')

$(form).submit() 的任何变体都执行本地提交,而不是远程提交。 在以前的版本中,jQuery 提交是远程而不是本地的。 我不知道在哪里记录了这一点。 The answer 我在上面链接解释了它 - 但这真的是 RoR 打算让您提交 ajax 表单的方式吗?我发现在这个意义上提交表单有“正确的方式”和“错误的方式”,这非常不直观。

更改了ajax:success 处理程序的签名。

同样,我没有找到解释此更改的任何地方,但我想这只是“rails-ujs 是如何做到的”。我不得不通过 ajax 处理程序从
  form.on('ajax:success', (event, response, status) => 
    $.each(response, (i, key) => 
      do_stuff();
    );
  );

  form.on('ajax:success', (event) => 
    const [response, _status, _xhr] = event.detail;
    $.each(response, (i, key) => 
      do_stuff();
    );
  );

再次,我猜这是因为事件是由 rails-ujs 而不是 jquery 处理的,但是......为什么?你怎么知道?

【问题讨论】:

你的 jquery 中有 preventDefault 吗? 不要去测试版,去第一个发布的版本——你可能已经在和一个固定的错误作斗争了。只需确保您遵循官方更新说明,并逐个版本进行更新即可。 @Int'lManOfCodingMystery 我已经添加了save() 调用的全文(用于提交表单)。它是在单击不是表单提交按钮的按钮时触发的(我不知道为什么)。事实上我去检查调用者并没有传入任何事件,所以我认为这个块没有做任何事情。我认为在提交事件中没有任何地方导致 preventDefault - 这就是您的意思吗? @BradWerth 说得很好,谢谢。我已经重做升级到 5.2.0 并且我看到了相同的行为。我会在问题中更改它以避免混淆。我确实尝试阅读the upgrade notes,但没有找到任何帮助。 【参考方案1】:

好的,有几件事要考虑。

本地:假而不是远程:真

首先,form_with 不再有remote 选项,它被称为本地:

<%= form_with model: record, url: client_field_value_path(@dataset, record), local: false do |form| %>
  <%= the form contents %>
<% end %>

此外,您应该可以完全跳过它,因为它是默认设置。您可以通过删除它然后检查指向此表单视图的路由的 html 标记来确认这一点,您应该在表单标记中看到 data-remote="true"。这可能不是问题。

你的 jQuery 代码在哪里?

通常,ujs 的工作方式是在视图目录中有一个名为create.js.erb 的文件。当您提交表单时,该文件中的 js 将运行,您将能够在该模板中使用 erb 标签将动态内容添加到 JS。例如,您可以在此处使用render 渲染部分并传入控制器中定义的实例变量。

当然,如果您已经单独设置了所有 javascript,并且它应该只在您的 javascript 使用时才能正常工作,那么它不再是 UJS。 UJS 代表不显眼的 javascript。这个想法是javascript增强功能而不是替换它。这意味着如果您的 javascript 由于某种原因没有加载,表单仍然可以在没有使用 javascript 的情况下提交并正常工作。您的应用可能并非如此,这很好,但知道提供更好的答案会很有用。

UJS 模式的工作方式依赖于一些控制器代码。因此,您的控制器中有一个respond_to 块,它将响应对 html 的请求和对 js 的请求。如果表单是远程的,那么它将在与控制器操作匹配的文件中运行 javascript。例如,

def create 
  @record = Record.new(record_params)
  respond_to do |format|
    if @record.save
      format.html  redirect_to records_path 
      format.js 
    else 
      format.html  render :new 
      format.js  render :error 
    end
  end
end
end

【讨论】:

感谢您的回答!它帮助我取得了一些进展 - 我将首先回答有帮助的部分。我注意到的第一个错误是$('form[data-remote=true]') 不再返回任何表单,就像以前一样。我误解了 remote 是“默认值”的想法,这意味着不再使用该属性。当我在视图中有remote: true 时,或者如果它是空白的,那么$('form[data-remote=true]') 是空的。但是,当我设置local: false 时,它会返回表单。现在,当我单击表单时,它会被提交到服务器。 ATM 表单挂起,但我可以进一步调查。 我们按照您描述的方式使用 Javascript。有一个文件夹app/assets/javacripts/modules,其中包含几个(~10)个临时脚本,这些脚本都加载到每个页面上。应用程序中没有太多 JS(这是它最复杂的应用程序)。在这个特定的控制器中,只有一个 JS 响应。因此,当我触发本地提交时,我会得到一个包含 JSON 内容的不受欢迎的纯文本页面。 我设法将自己修补到一个似乎有效的解决方案。我真的不明白为什么我所做的改变/是必要的。您似乎更清楚地了解情况,因此,如果您对我上面所做的更改有任何建议,我将不胜感激!【参考方案2】:

我现在明白这个问题了!我以前很困惑。

答案

由于某种原因(我仍然不明白),

Rails.application.config.action_view.form_with_generates_remote_forms

在升级后未设置。因为它没有设置,data-remote="true" 属性在 HTML 中不存在,所以 jquery-ujs 没有使用 AJAX 提交表单。在我的初始化中将此配置变量设置为 true 解决了这个问题。

题外话

我很困惑地认为rails-ujsjquery-ujs 的替代品。它不是。他们以非常不同的方式工作! jquery-ujs 拦截通过 jquery 调用的事件,以(例如)获取要使用 AJAX 提交的远程表单。 rails-ujs 通过创建自己的事件并使用Rails.fire 调用它们具有相同的功能效果。他们在一起玩得不好!

我在添加 rails-ujs 后得到的所有困惑都源于它们基本上是具有不同 API 的不同库。

【讨论】:

以上是关于升级到 rails 5.2 - 远程表单现在刷新页面的主要内容,如果未能解决你的问题,请参考以下文章

将 Rails 5.2 升级到 6.0.0 后内存泄漏

Rails 5.2 数据表不会在页面更改时刷新数据

在 Rails 5.2 中禁用活动存储

如何在 ActiveStorage (Rails 5.2) 中更新附件

Rails 5.2 上 jquery DataTables 和 will_paginate gem 的分页问题

Ruby on Rails 链接到无效输入后表单失败的上一页