如何通过在Rails上启用Turbolinks的AJAX正确呈现部分?

时间:2017-10-03 17:58:52

标签: jquery ruby-on-rails ajax ruby-on-rails-5 turbolinks

我目前正在开发一个Rails应用程序,该应用程序必须与外部Web API通信,从结果中更新DB记录并异步更新视图。

我正在做什么

我有一个list操作,我直接从数据库中显示数据,其中包含一个在分页表中发送的电子邮件列表。呈现视图后,我需要向应用程序(在不同的路径上)发出一个AJAX请求,该请求向外部Web API发出请求,更新数据库,然后使用包含所有内容的table呈现部分电子邮件,与原始list操作中呈现的电子邮件相同。

以下代码处理AJAX请求:

# _mail_list_loader.js.erb
document.addEventListener("turbolinks:load", function() {
  $.ajax({
    url: '/certified_mail/mail_list',
    type: 'get',
    data: {
      page: <%= @page %>,
      rut: <%= @rut || 'null' %>,
      from: <%= @from || 'null' %>,
      to: <%= @to || 'null' %>,
      ids: <%= @outdated_message_ids.to_json.html_safe %>
    }
  });
});

哪个回复是.js.erb,它会呈现实际table的部分内容:

# mail_list.js.erb
$('#mail-list').html('<%= j render 'certified_mail/mail_list' %>');

_mail_list_loader.js.erb呈现application.html.erb

# application.html.erb
<head>
    ...
    <script><%= render 'certified_mail/mail_list_loader.js' %></script>
</head>

对于首先加载表的操作和处理AJAX请求的操作,我的控制器看起来像这样:

class CertifiedMailController < ApplicationController
  def list
    if params[:page].nil? || params[:page].to_i <= 0
      redirect_to action: :list, params: {
        page: 1,
        from: params[:from].to_s.empty? ? nil : params[:from],
        to: params[:to].to_s.empty? ? nil : params[:to]
      }
    else
      # Handle filters and get emails from the DB
    end
  end

  def mail_list
    if request.xhr?
      # Make API request, update accordingly,
      # handle filters and get emails from the DB
      respond_to do |format|
        format.js
      end
    else
      redirect_to action: :list
    end
  end
end

我的问题是什么

当页面首次加载时,它从控制器加载,并且AJAX请求被正确发送,但如果我转到下一页(发送具有不同GET参数page的请求),页面直接从控制器加载,但是发送了两个AJAX请求,一个用于第一页,一个用于第二页,如下所示,并且table有点的HTML随机更改,而不是真正显示正确的数据table

Network log from Firefox

我曾经有过JavaScript,它在body而不是header中定义了AJAX请求,但是这样的请求只会在每次更改页面时保持重复,每次请求都会有越来越多的请求时间。我做了一些搜索,发现通过将代码移动到head可以修复额外调用的问题。

我真的不知道我的问题是否与Turbolinks处理请求的方式有关,但我需要的是应用程序呈现完整视图然后发送完全相同的GET的AJAX请求params到一个不同的动作,只是用表格呈现部分。

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

由于_mail_list_loader.js.erb中有动态内容,您可能会注意到<head>中的脚本标记在后续页面加载时被复制。虽然它是作为一个代码块编写的,但Turbolinks并没有&#34;看到&#34;就这样,并在每个负载上附加一个新的脚本元素。

我建议您尝试将尽可能多的JavaScript移动到.js文件中并将其包含在应用程序清单中。您可以通过将它们作为属性呈现来获取动态服务器生成的变量,并让JS选择它们。

例如,您可能想要尝试以下内容:

# app/views/certified_mail/list.html.erb
<% props = { page: @page, rut: @rut, from: @from, to: @to, ids: @outdated_message_ids } %>
<table data-component="mail-list" data-props="<%= props.to_json %>" id="mail_list_<%= @page %>">
 …
</table>
# app/assets/javascripts/load_mail_list.js
;(function () {
  function loadMailList (data, callback) {
    $.ajax({
      url: '/certified_mail/mail_list',
      type: 'get',
      dataType: 'html',
      data: data,
      success: callback
    })
  }

  $(document).on('turbolinks:load', function () {
    var $element = $('[data-component=mail-list]')
    loadMailList($element.data('props'), function (html) {
      $element.html(html)
    })
  })
})()
# app/controllers/certified_mail_controller.rb
def mail_list
  if request.xhr?
    # Make API request, update accordingly,
    # handle filters and get emails from the DB
    render 'certified_mail/mail_list'
  else
    redirect_to action: :list
  end
end

回调应确保响应将列表呈现为正确的元素(而在此之前,如果某人快速分页,则可能存在第3页上呈现第2页列表的风险)。

希望有所帮助。

答案 1 :(得分:0)

从我在this answer中看到的,在某些链接上禁用Turbolinks正是我为了完全重新加载我的JavaScript所需要的,所以我去了发送请求更改参数的分页链接(例如{{ 1}})并简单地将do { ... } while((again == 'y') || (again == 'Y')); 添加到其中。

现在没有额外的AJAX调用,因为链接现在只渲染整个HTML,而不是通过Turbolinks。