Rails:无法通过Ajax加载的远程表单提交

8

目标

我有一个页面,其中包含来自Rails后端的项目列表。我想使用Rails UJS通过Ajax调用编辑该列表中的一行。

方法

我在每一行的末尾添加了一个编辑按钮。编辑按钮是一个link_to ... :remote => true。单击它会重新加载列表,但选定的行变为编辑模式。可编辑行嵌入在一个form ... :remote => true中。该行中的保存按钮是submit按钮。

index.html.haml

#editor
  %table
    - @items.each do |item|
      %tr
        = render :partial => 'row', :locals => { :item => item }

_row.html.haml

... 
%td // a number of columns with attributes
...
%td
  = link_to t("actions.edit"), edit_item_path(item), :remote => true
  = link_to t("actions.delete"), item_path(item), :remote => true, :method => :delete, :data => { :confirm => "Are you sure?" }

edit.html.haml

#editor
  %table
    - @items.each do |item|
      %tr
        - if item == @item
          = form_for @item, :url => item_path(@item), :remote => true, do |f|
            = render :partial => "row_form", :locals => { :f => f }
        - else
          = render :partial => 'row', :locals => { :item => item }

_row_form.html.haml

... 
%td // a number of columns with editable attributes
...
%td
  %button{ :type => "submit" }=t("actions.save")
  = link_to t("actions.cancel"), items_path, :remote => true

Ajax响应处理

$("#editor").on("ajax:success", function(event, data, status, xhr) {
  $("#editor").html($(data).find("#editor").html());
});

问题

当我在编辑模式下加载列表页/items/12/edit时,第12项的行可编辑。单击保存按钮可以通过Ajax正确提交表单并使用jQuery加载/items索引部分,替换可编辑列表。再次单击编辑按钮会重新加载编辑页面(例如/items/12/edit),嵌入表单。但是,这一次,当单击保存按钮时,表单不再被提交。似乎没有将提交事件处理程序附加到动态加载的远程表单。

问题

如何提交通过Ajax加载的远程表单,最好使用Rails UJS方法?

重复

我知道有这个问题的重复,但没有一个得到了答案。我希望有人最终能给出一个明确的答案。


1
你的开发工具窗口中是否出现任何错误?如果有,它们是什么?此外,如果你还没有这样做,我建议你下载RailsPanel(如果你使用Chrome浏览器)。它可以为你所做的Rails请求提供一些非常好的反馈。 - Jake Smith
看了你的名为“Ajax响应处理”的部分,我认为你可能想尝试使用事件委托,就像他们在这个页面底部所示的那样。当然,这意味着你需要使用类属性而不是id属性来标识你的编辑表单。 - Jake Smith
5个回答

3
问题在于DOM被JavaScript和回调函数(例如在ajax成功时)修改,而回调函数仅在DOM初始化时初始化。因此,当DOM更改时(在#editor的一部分中),回调函数将不起作用。
因此,每次使用JavaScript更改DOM时,都应重新初始化此回调函数。
$("#editor").on("ajax:success", function(event, data, status, xhr) {
  $("#editor").html($(data).find("#editor").html());
});

一段时间以前,jQuery有一个名为“live”的函数。它的作用类似于“on”,但可以跟踪DOM的更改。但是,在当前版本的jQuery中,由于其速度较慢,此函数已被弃用。

希望您能理解我的糟糕英语 =)


2

我找到了问题!感谢@Jake Smith和@Parandroid的答案中提供的提示。这是我分成两步发现的。

寻找1

虽然让ajax:success被触发似乎不是问题,但看起来表单处理不完全正确,只在$("#editor")选择器上。至少需要$("#editor form"),但如果我们从索引页面开始,该方法可能无法正常工作,因为该页面尚未包含表单。因此,@Jake Smith建议的方法似乎是最强大的方法。 这导致:

edit.html.haml

#editor
  = render :partial => "edit"

edit.js.erb

$('#editor').html('<%= escape_javascript render("edit") %>');

_edit.html.haml (still not working)

%table
  - @items.each do |item|
    %tr
      - if item == @item
        = form_for @item, :url => item_path(@item), :remote => true, do |f|
          = render :partial => "row_form", :locals => { :f => f }
      - else
        = render :partial => 'row', :locals => { :item => item }

但是这仍然没有使提交按钮正常工作。直到我发现了问题所在...

发现2

上面的解决方案确实使提交按钮具有了普通的POST行为,但服务器不喜欢这样做,因为它期望达到update操作需要使用PUT。Rails通过生成带有值PUT的隐藏字段_method来实现此目标。我发现Rails会在_edit.html.haml部分的最上面生成此字段(以及其他几个关键的隐藏字段),而不是form标签内部!因此,我将表单移到了部分的顶部,它就可以工作了!

_edit.html.haml(工作中!)

= form_for @item, :url => item_path(@item), :remote => true, do |f|
  %table
    - @items.each do |item|
      %tr
        - if item == @item
          = render :partial => "row_form", :locals => { :f => f }
        - else
          = render :partial => 'row', :locals => { :item => item }

谁能想到...

关于IT技术的内容


你的问题是由于HTML规则不允许在表格内放置表单。那就是为什么你必须将form_for移到外面。请参考这个链接https://dev59.com/QG025IYBdhLWcg3wf2OE - Juan Pablo Ugas
我想补充一下,我在使用纯Rails时也遇到了同样的问题(你知道所有那些与Haml和Simple Forms有关的东西对于不使用它们的人来说相当分散注意力)。我的表单被包裹在一些div中,所以在去掉这些包装后,它神奇地开始工作了!非常感谢! - Alexander Gorg

2
我成功实现的方法是使用Rails内置的功能:
  1. 在您的控制器的编辑操作中,将 format.js 添加到 respond_to 块中。这允许您创建一个名为edit.js.erb的文件,用于将编辑表单加载到DOM中。
  2. 然后在相同控制器的更新操作中,再次将 format.js 添加到 respond_to 块中,并有一个名为update.js.erb的文件,通过jQuery进行必要的DOM操作。
我确定在线教程进一步解释了这个过程,但是在不必调用jQuery ajax方法的情况下理解它是在CodeSchool.com上实现的。

你具体遇到了什么错误?你知道这种方法执行到哪里就停止工作了吗? - Jake Smith
那就直接链接到CodeSchool的根域名吧,因为那非常有用...嗯不对。 - LpLrich
可能不适合您。但是链接的目的是为他们提供注册的起点,并找到他们想要参加的课程。如果我链接到特定的课程,他们仍然需要注册。但感谢您有用的评论。 - Jake Smith
没问题,随时可以。 :) - LpLrich

0

我曾经遇到过类似的问题,即通过ajax加载的表单无法提交,这是由于Turbolinks引起的。我发现解决方法是将事件绑定到body而不是元素上,相关答案可以在以下主题中找到:

如何在<a标签上使用$(document).on("click..

对我来说,问题出在点击标签上,所以我不得不更改

$('label').click(function...

$('body').on('click', 'label', function


-1

我曾经遇到过同样的问题,最终明白了原因。 你的HTML源代码必须按照以下方式组成。

<tr>
  <form>
    <td><input type="text"></td>
    <td><input type="submit"></td>
  </form>
</tr>

看起来一些浏览器,包括Chrome和Firefox无法正确处理这样的嵌套标签。

在将表格/tr/td标签替换为其他标签(如ul/li)后,我成功地提交了重新渲染的表单。


请注意,您的HTML代码完全无效。form元素不能是tr的直接子元素。 - Rory McCrossan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接