当表单中存在文件字段时,Rails 不发出 Ajax 请求

Posted

技术标签:

【中文标题】当表单中存在文件字段时,Rails 不发出 Ajax 请求【英文标题】:Rails not making Ajax request when file field is present in form 【发布时间】:2021-03-15 02:05:24 【问题描述】:

问题总结

根据the docs,在使用form_with时,Rails默认发出Ajax请求。我需要一个 Ajax 请求,但我发现了一种情况,改为发出标准浏览器请求。我很想知道我做错了什么以及如何解决它。

期望行为:在提交表单时发出 Ajax 请求,因此我可以返回 javascript,它只会更改显示页面的一部分。

当前行为:标准浏览器请求发出 404 秒,因为该路由仅适用于 XHR。 (如果我让所有请求都可以访问该路由,则整个页面都会被替换,这仍然是非常不可取的。)

代码

这是生成表单的代码:

<%= form_with(url: restore_path,
              method: 'post',
              multipart: true,
              id: 'restore-form') do %>

  <label for="file">Backup file:</label>
  <%= file_field_tag 'backup_file', required: true %>

  <%= submit_tag('Restore',
                 id: 'restore-submit-button',
                 class: 'btn btn-primary',
                 data:  disable_with: 'Restoring...' ) %>

<% end %>

但是,这不会产生 Ajax 请求。 restore_path 是一条仅限 XHR 的路由...

  constraints(->(req)  req.xhr? ) do
    # other routes snipped for brevity's sake
    post 'restore' => 'backups#restore'
  end

...提交表单会导致 404,因为该请求是标准浏览器请求。

我在尝试调试时发现的一个奇怪现象

如果我将字段从 file_field_tag 更改为 text_field_tag,请求将作为 Ajax 提交。当我改变时

<%= file_field_tag 'file', required: true %>

<%= text_field_tag 'test_field', nil, required: true %>

其他一切都保持不变,一个 Ajax 请求按预期发出。生成的 html 的唯一变化(除了真实性令牌的确切值)是

<input type="file" name="file" id="file" required="required">

更改为

<input type="text" name="test_field" id="test_field" required="required">

...正如人们所期望的那样。但是表单提交行为完全改变了。

回购

项目是开源的;上述代码在https://github.com/steve-mcclellan/j-scorer/blob/master/app/views/stats/_options.html.erb

我尝试过的另一件事

我从头开始创建了一个小型的新 Rails 应用程序,以查看是否可以重现该问题,但我不能。即使存在文件上传,也会发出 Ajax 请求。

哈哈!

我的想法已经不多了。我究竟做错了什么?当文件字段存在时,如何让我的恢复表单发出 Ajax 请求?

【问题讨论】:

【参考方案1】:

知道了。文档假设我正在使用 rails-ujs 适配器来实现不显眼的 JavaScript(自 Rails 5.1 开始内置)。我的应用是在 Rails 4 时代创建的,使用旧的 jquery-ujs 适配器。

浏览器本身不会发送包含文件上传的 Ajax 请求。 rails-ujs 有一个内置的解决方法,但 jquery-ujs 没有。

从这里开始有两种方法。

长期的解决方案是切换到rails-ujs,目前作为 Rails 的一部分进行维护。 (在撰写本文时,jquery-ujs 已经两年半没有更新了。)

不幸的是,这两个适配器有一些接口差异,因此可能需要在整个应用程序的 JavaScript 中进行一些更改。详情请见this article。

快速修复?好吧,有a gem。 (文档表明它适用于 Rails 3,但它对我的应用程序很有吸引力,在撰写本文时它正在运行 Rails 6.0.3.3。)

除了Gemfile

gem 'remotipart', '~> 1.4.4'

添加到app/assets/javascripts/application.js(在现有的jquery_ujs 行之后):

//= require jquery.remotipart

而且它有效。该请求是通过 Ajax 发出的。它可以毫无问题地访问我的仅限 XHR 的路线,并呈现仅更新部分页面的 JavaScript 视图。

胜利!

【讨论】:

//= require rails-ujs 不能代替 jquery_ujs 工作吗? @Eyeslandic 它可以工作,但jquery_ujsrails-ujs 之间的接口有点不同,所以我需要对现有代码进行多项更改,如下所述: dev.to/nejremeslnici/migrating-from-jquery-ujs-to-rails-ujs-k9m。在某些时候这样做是个好主意,所以我使用的是更新的库,但remotipart gem 是一个不需要任何进一步更改的插入式补丁。这就是我现在所需要的,至少。 好的,如果它对你有用,那就太好了。 @Eyeslandic 感谢您的意见。我已经编辑了答案以反映这两个选项以及它们的优缺点。

以上是关于当表单中存在文件字段时,Rails 不发出 Ajax 请求的主要内容,如果未能解决你的问题,请参考以下文章

当文本字段填充多对多表单时,Rails 3.2 Ajax 更新 Div

如果 Rails 表单中的另一个字段为空,则验证嵌套字段

解析rails中的表单隐藏字段

当条目字段不可见时,如何隐藏 Xamarin 表单中的错误标签?

在 Rails 6 中使用 activestorage 时,如何在重新显示表单时保留文件?

在提交失败时保持 jQuery - Rails