升级到 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的情况,所以有点迷茫。
编辑:完整的保存调用 - 当单击“保存”按钮时触发(但该按钮不是 <input type=submit>
,它只是一个样式按钮)。
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
处理程序的签名。
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-ujs
是jquery-ujs
的替代品。它不是。他们以非常不同的方式工作! jquery-ujs 拦截通过 jquery 调用的事件,以(例如)获取要使用 AJAX 提交的远程表单。 rails-ujs 通过创建自己的事件并使用Rails.fire
调用它们具有相同的功能效果。他们在一起玩得不好!
我在添加 rails-ujs 后得到的所有困惑都源于它们基本上是具有不同 API 的不同库。
【讨论】:
以上是关于升级到 rails 5.2 - 远程表单现在刷新页面的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ActiveStorage (Rails 5.2) 中更新附件