在 Rails 中通过轮询刷新多个部分
Refreshing multiple partials with polling in Rails
假设我有一个可能如下所示的状态列表:
ul#list
- @list_items.each do |item|
li.loading Item #{item.id} - Status: #{item.status}
li Item #{item.id} - Status: #{item.status}
li Item #{item.id} - Status: #{item.status}
li.loading Item #{item.id} - Status: #{item.status}
这使我:
Item 1 - Status: Loading
Item 2 - Status: Finished
Item 3 - Status: Finished
Item 4 - Status: Loading
我想做的是定期轮询个人 列表项的更改,并在状态更改时刷新它们。到目前为止,我能够摆脱刷新整个列表的麻烦:
ul#list
== render 'status_list', list_items: @list_items
咖啡:
if $('.loading').length > 0
setInterval (=>
@refreshListPartial()
), 5000
其中 @refreshListPartial
是一个 AJAX 函数,它点击 Rails 控制器,然后继续刷新整个列表部分:
$("#list").html("<%= escape_javascript(render partial: 'status_list', locals: { list_items: @list_items } ) %>");
但是要如何检查页面上 individual
列表项的状态并只刷新它们呢?我知道 React 可能是完成此任务的更简单的解决方案,但是否有可能使用 Rails 来完成而无需跳过一打箍?想到的另一件事是 ActionCable
(我正在使用 Rails 5),但是为此功能打开永久连接似乎有点矫枉过正,我宁愿选择轮询。
更新
只是大声思考。所以要刷新多个局部而不是一个,我需要在我的 .js.erb 文件中到达这个:
<%- @items.each do |item| %>
$("#list-item-<%= item.id %>").html("<%= escape_javascript(render partial: 'item', locals: { list_item: item } ) %>");
<% end %>
视图现在应该如下所示:
ul#list
@list_items.each do |item|
== render 'list_item', list_item: @list_item
所以剩下的是 ajax 函数,它应该获取需要刷新的列表项的 ID,并将它们作为数组发送到控制器。
我最终扩展了我自己在问题更新中提出的内容。
根据数据标签检查是否需要刷新某些部分的前端代码:
class JobRequestPartialReload
constructor: ->
checkForRunningJobs = ->
if $('.poll_for_changes').length > 0
@arr = []
$('.poll_for_changes').each (index, element) =>
@arr.push(element.closest('[data-id]').dataset.id)
sendDataToRails()
sendDataToRails = ->
$.ajax
url: "/jobs/refresh_list"
method: "POST"
data: {jobRequestList: @arr}
setInterval (=>
checkForRunningJobs()
), 10000
$(document).on 'turbolinks:load', new JobRequestPartialReload
控制器:
def refresh_list
ajax_data = params[:jobRequestList].map(&:to_i)
@job_requests = JobRequest.includes(...).where(id: ajax_data)
respond_to do |format|
format.js
end
end
最后,JS.erb 文件:
<% @job_requests.each do |job| %>
<% case job.state %>
<% when 'initiated' %>
// don't care
<% when 'active' %>
visibleStatus = $('#job-id-<%= job.id %> .status-list').text()
if (visibleStatus == 'initiated') {
$('#job-id-<%= job.id %>').html("<%= escape_javascript(render partial: 'job_requests/shared/job_request', locals: { job: job } ) %>");
<% else %>
// failed, completed, etc.
$('#job-id-<%= job.id %>').html("<%= escape_javascript(render partial: 'job_requests/shared/job_request', locals: { job: job } ) %>");
<% end %>
<% end %>
回复更新
我后来添加了 js 代码来检查某些部分是否在实际用户视口中,并且只检查它们,以 5-10 秒的速度。这大大减少了每个客户端发送的查询数量。
假设我有一个可能如下所示的状态列表:
ul#list
- @list_items.each do |item|
li.loading Item #{item.id} - Status: #{item.status}
li Item #{item.id} - Status: #{item.status}
li Item #{item.id} - Status: #{item.status}
li.loading Item #{item.id} - Status: #{item.status}
这使我:
Item 1 - Status: Loading
Item 2 - Status: Finished
Item 3 - Status: Finished
Item 4 - Status: Loading
我想做的是定期轮询个人 列表项的更改,并在状态更改时刷新它们。到目前为止,我能够摆脱刷新整个列表的麻烦:
ul#list
== render 'status_list', list_items: @list_items
咖啡:
if $('.loading').length > 0
setInterval (=>
@refreshListPartial()
), 5000
其中 @refreshListPartial
是一个 AJAX 函数,它点击 Rails 控制器,然后继续刷新整个列表部分:
$("#list").html("<%= escape_javascript(render partial: 'status_list', locals: { list_items: @list_items } ) %>");
但是要如何检查页面上 individual
列表项的状态并只刷新它们呢?我知道 React 可能是完成此任务的更简单的解决方案,但是否有可能使用 Rails 来完成而无需跳过一打箍?想到的另一件事是 ActionCable
(我正在使用 Rails 5),但是为此功能打开永久连接似乎有点矫枉过正,我宁愿选择轮询。
更新
只是大声思考。所以要刷新多个局部而不是一个,我需要在我的 .js.erb 文件中到达这个:
<%- @items.each do |item| %>
$("#list-item-<%= item.id %>").html("<%= escape_javascript(render partial: 'item', locals: { list_item: item } ) %>");
<% end %>
视图现在应该如下所示:
ul#list
@list_items.each do |item|
== render 'list_item', list_item: @list_item
所以剩下的是 ajax 函数,它应该获取需要刷新的列表项的 ID,并将它们作为数组发送到控制器。
我最终扩展了我自己在问题更新中提出的内容。
根据数据标签检查是否需要刷新某些部分的前端代码:
class JobRequestPartialReload
constructor: ->
checkForRunningJobs = ->
if $('.poll_for_changes').length > 0
@arr = []
$('.poll_for_changes').each (index, element) =>
@arr.push(element.closest('[data-id]').dataset.id)
sendDataToRails()
sendDataToRails = ->
$.ajax
url: "/jobs/refresh_list"
method: "POST"
data: {jobRequestList: @arr}
setInterval (=>
checkForRunningJobs()
), 10000
$(document).on 'turbolinks:load', new JobRequestPartialReload
控制器:
def refresh_list
ajax_data = params[:jobRequestList].map(&:to_i)
@job_requests = JobRequest.includes(...).where(id: ajax_data)
respond_to do |format|
format.js
end
end
最后,JS.erb 文件:
<% @job_requests.each do |job| %>
<% case job.state %>
<% when 'initiated' %>
// don't care
<% when 'active' %>
visibleStatus = $('#job-id-<%= job.id %> .status-list').text()
if (visibleStatus == 'initiated') {
$('#job-id-<%= job.id %>').html("<%= escape_javascript(render partial: 'job_requests/shared/job_request', locals: { job: job } ) %>");
<% else %>
// failed, completed, etc.
$('#job-id-<%= job.id %>').html("<%= escape_javascript(render partial: 'job_requests/shared/job_request', locals: { job: job } ) %>");
<% end %>
<% end %>
回复更新
我后来添加了 js 代码来检查某些部分是否在实际用户视口中,并且只检查它们,以 5-10 秒的速度。这大大减少了每个客户端发送的查询数量。