使用 Action Cable 更新状态 table
Update presence table with Action Cable
对于具有 Devise I implemented the presence example of https://guides.rubyonrails.org/action_cable_overview.html#example-1-user-appearances 的 Rails 5.2.2 应用程序和以下文件:
app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data['appearing_on'])
end
def away
current_user.away
end
end
app/assets/javascripts/cable/subscriptions/appearance.咖啡
App.cable.subscriptions.create "AppearanceChannel",
# Called when the subscription is ready for use on the server.
connected: ->
@install()
@appear()
# Called when the WebSocket connection is closed.
disconnected: ->
@uninstall()
# Called when the subscription is rejected by the server.
rejected: ->
@uninstall()
appear: ->
# Calls `AppearanceChannel#appear(data)` on the server.
@perform("appear", appearing_on: $("main").data("appearing-on"))
away: ->
# Calls `AppearanceChannel#away` on the server.
@perform("away")
buttonSelector = "[data-behavior~=appear_away]"
install: ->
$(document).on "turbolinks:load.appearance", =>
@appear()
$(document).on "click.appearance", buttonSelector, =>
@away()
false
$(buttonSelector).show()
uninstall: ->
$(document).off(".appearance")
$(buttonSelector).hide()
然后我将以下两种方法添加到我的 users
模型中,以在用户存在或不存在时更新 is_present
属性。
app/models/users.rb
[...]
def appear
self.update_attributes(is_present: true)
end
def disappear
self.update_attributes(is_present: false)
end
[...]
在索引页上 main#index
我显示所有用户的列表及其在线状态:
app/controllers/main_controller.rb
[...]
def index
@users = User.order(:last_name)
end
[...]
app/views/main/index.html.erb
<h1>Users</h1>
<%= render partial: "presence_table", locals: {users: @users} %>
app/views/main/_presence_table.html.erb
<div class="presence-table">
<table class="table table-striped">
<thead>
<tr>
<th><%= User.human_attribute_name("last_name") %></th>
<th><%= User.human_attribute_name("is_present") %></th>
</tr>
</thead>
<tbody>
<% users.each do |user| %>
<tr>
<td><%= user.last_name %></td>
<td><%= user.is_present %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
问题
当用户的存在发生变化时,如何使用 Action Cable 自动更新 table?根据我的理解,现有的部分应该是可能的,但我不知道该怎么做。
我不希望用户必须重新加载 main#index
页面才能获得最新的状态列表,而是在用户状态发生变化时推送 _presence_table.html.erb
的内容。
在我看来,当客户端连接或将自己设置为离开时,您实际上只是在更新数据库中的用户在线状态。
你还需要将这些事件广播到频道,其他客户端需要监听这些事件并操作DOM。
在客户端,您需要在 AppearanceChannel
中实现 received -> (data)
方法。每次服务器向该频道的订阅者发送事件时都会调用它。
这里是纯 JS 代码:
App.presence = App.cable.subscriptions.create("AppearanceChannel", {
received: (data) => {
// here you have access to the parameters you send server side, e.g. event and user_id
let presenceEl = document.getElementById(data.user_id).querySelectorAll("td")[1]
if (data.event == "disappear") {
presenceEl.innerHtml = "false"
} else {
presenceEl.innerHtml = "true"
}
}
})
并且在 app/models/users.rb
的服务器上
def appear
self.update_attributes(is_present: true)
ActionCable.server.broadcast("appearance_channel", event: "appear", user_id: id)
end
def disappear
self.update_attributes(is_present: false)
ActionCable.server.broadcast("appearance_channel", event: "disappear", user_id: id)
end
对于具有 Devise I implemented the presence example of https://guides.rubyonrails.org/action_cable_overview.html#example-1-user-appearances 的 Rails 5.2.2 应用程序和以下文件:
app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data['appearing_on'])
end
def away
current_user.away
end
end
app/assets/javascripts/cable/subscriptions/appearance.咖啡
App.cable.subscriptions.create "AppearanceChannel",
# Called when the subscription is ready for use on the server.
connected: ->
@install()
@appear()
# Called when the WebSocket connection is closed.
disconnected: ->
@uninstall()
# Called when the subscription is rejected by the server.
rejected: ->
@uninstall()
appear: ->
# Calls `AppearanceChannel#appear(data)` on the server.
@perform("appear", appearing_on: $("main").data("appearing-on"))
away: ->
# Calls `AppearanceChannel#away` on the server.
@perform("away")
buttonSelector = "[data-behavior~=appear_away]"
install: ->
$(document).on "turbolinks:load.appearance", =>
@appear()
$(document).on "click.appearance", buttonSelector, =>
@away()
false
$(buttonSelector).show()
uninstall: ->
$(document).off(".appearance")
$(buttonSelector).hide()
然后我将以下两种方法添加到我的 users
模型中,以在用户存在或不存在时更新 is_present
属性。
app/models/users.rb
[...]
def appear
self.update_attributes(is_present: true)
end
def disappear
self.update_attributes(is_present: false)
end
[...]
在索引页上 main#index
我显示所有用户的列表及其在线状态:
app/controllers/main_controller.rb
[...]
def index
@users = User.order(:last_name)
end
[...]
app/views/main/index.html.erb
<h1>Users</h1>
<%= render partial: "presence_table", locals: {users: @users} %>
app/views/main/_presence_table.html.erb
<div class="presence-table">
<table class="table table-striped">
<thead>
<tr>
<th><%= User.human_attribute_name("last_name") %></th>
<th><%= User.human_attribute_name("is_present") %></th>
</tr>
</thead>
<tbody>
<% users.each do |user| %>
<tr>
<td><%= user.last_name %></td>
<td><%= user.is_present %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
问题
当用户的存在发生变化时,如何使用 Action Cable 自动更新 table?根据我的理解,现有的部分应该是可能的,但我不知道该怎么做。
我不希望用户必须重新加载 main#index
页面才能获得最新的状态列表,而是在用户状态发生变化时推送 _presence_table.html.erb
的内容。
在我看来,当客户端连接或将自己设置为离开时,您实际上只是在更新数据库中的用户在线状态。
你还需要将这些事件广播到频道,其他客户端需要监听这些事件并操作DOM。
在客户端,您需要在 AppearanceChannel
中实现 received -> (data)
方法。每次服务器向该频道的订阅者发送事件时都会调用它。
这里是纯 JS 代码:
App.presence = App.cable.subscriptions.create("AppearanceChannel", {
received: (data) => {
// here you have access to the parameters you send server side, e.g. event and user_id
let presenceEl = document.getElementById(data.user_id).querySelectorAll("td")[1]
if (data.event == "disappear") {
presenceEl.innerHtml = "false"
} else {
presenceEl.innerHtml = "true"
}
}
})
并且在 app/models/users.rb
def appear
self.update_attributes(is_present: true)
ActionCable.server.broadcast("appearance_channel", event: "appear", user_id: id)
end
def disappear
self.update_attributes(is_present: false)
ActionCable.server.broadcast("appearance_channel", event: "disappear", user_id: id)
end