订阅 Action Cable 频道时如何设置参数

Posted

技术标签:

【中文标题】订阅 Action Cable 频道时如何设置参数【英文标题】:How to set params when subscribing to Action Cable channel 【发布时间】:2018-02-08 19:38:26 【问题描述】:

似乎几个月来,我一直在努力研究动作电缆。请帮忙。

我有一个“连接” - 我无法设置 identified_by :current_user,因为此端点还需要由使用 WebSockets 的外部 API 使用。无法使用浏览器 cookie 来验证 API 端点。

文件和支持

连接:/app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base

  end
end

频道:/app/channels/application_cable/channel.rb

module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end

我有一个特定的访问频道:/app/channels/visits_channel.rb

class VisitChannel < ApplicationCable::Channel
  def subscribed
    stream_from "visit_#params[:visit_id]"
  end
end

然后我有我的咖啡脚本频道:/app/assets/javascripts/channels/visit.coffee

App.visit = App.cable.subscriptions.create  channel: 'VisitChannel', visit_id: '42' ,
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    console.log data

  push: ->
    @perform 'push'

然后我的访问模型有回调:/app/models/visit.rb

class Visit < ApplicationRecord

  after_save  :push_to_action_cable

  **** detail of model removed ****

  def push_to_action_cable
    ActionCable.server.broadcast("visit_#self.id", self)
  end

end

这是完美的工作,它每次都将对象放入控制台,并且只有 ID 为 42 的对象

这是我的问题:

在coffeescript 频道中:位于/app/assets/javascripts/channels/visit.coffee - 我如何设置visit_id 以便仅在我想要的访问中“收听”更改?

App.visit = App.cable.subscriptions.create  channel: 'VisitChannel', visit_id: 'HOW_DO_I_SET_THIS?' ,
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    console.log data

  push: ->
    @perform 'push'

我尝试过的:

我已经尝试了各种变化,例如:

App.visit = App.cable.subscriptions.create  channel: 'VisitChannel', visit_id: <%= @visit.id %> 

结果:

ExecJS::RuntimeError in Visits#action_cable
Showing /Users/johnsalzarulo/code/uvohealth/app/views/layouts/application.html.erb where line #9 raised:

SyntaxError: [stdin]:1:81: unexpected <

App.visit = App.cable.subscriptions.create (channel: 'VisitChannel', visit_id: "# params[:id] ")

结果:

ExecJS::RuntimeError in Visits#action_cable
Showing /Users/johnsalzarulo/code/uvohealth/app/views/layouts/application.html.erb where line #9 raised:

SyntaxError: [stdin]:1:93: unexpected :

App.visit = App.cable.subscriptions.create (channel: 'VisitChannel', visit_id: "# @visit.id ")

结果:

visit.self-e04de4513d06884493c48f4065f94d23255be682f915e26766c54bb9d17ef305.js?body=1:4 Uncaught TypeError: Cannot read property 'id' of undefined
    at visit.self-e04de4513d06884493c48f4065f94d23255be682f915e26766c54bb9d17ef305.js?body=1:4
    at visit.self-e04de4513d06884493c48f4065f94d23255be682f915e26766c54bb9d17ef305.js?body=1:18
(anonymous) @ visit.self-e04de4513d06884493c48f4065f94d23255be682f915e26766c54bb9d17ef305.js?body=1:4
(anonymous) @ visit.self-e04de4513d06884493c48f4065f94d23255be682f915e26766c54bb9d17ef305.js?body=1:18

App.visit = App.cable.subscriptions.create (channel: 'VisitChannel', visit_id: "# visit.id ")

结果:

visit.self-b636f38376edc085c15c2cfc4d524bafc5c5163a8c136b80ba1dda12813fc0b5.js?body=1:4 Uncaught ReferenceError: visit is not defined
    at visit.self-b636f38376edc085c15c2cfc4d524bafc5c5163a8c136b80ba1dda12813fc0b5.js?body=1:4
    at visit.self-b636f38376edc085c15c2cfc4d524bafc5c5163a8c136b80ba1dda12813fc0b5.js?body=1:18
(anonymous) @ visit.self-b636f38376edc085c15c2cfc4d524bafc5c5163a8c136b80ba1dda12813fc0b5.js?body=1:4
(anonymous) @ visit.self-b636f38376edc085c15c2cfc4d524bafc5c5163a8c136b80ba1dda12813fc0b5.js?body=1:18

结束时

我尝试了更多的组合。唯一的工作就是将&lt;script&gt; 扔到显式订阅访问的那个页面的视图模板中,但是我没有得到回调的好处,而且我知道这不是“rails”大大地”。

阅读这些文档并尝试完成这项工作已经花费了数小时。任何人都可以阐明我在这里缺少的东西吗?

【问题讨论】:

有同样的问题! 【参考方案1】:

在 HTML 标记中设置visit_id,可能在布局文件中的body 标记中。

<body data-visit-id="<%= @visit.id %>">

现在像这样从 JS 中读取它:

document.querySelector('body').dataset.visitId

您的订阅创建行如下所示:

App.visit = App.cable.subscriptions.create (channel: 'VisitChannel', visit_id: document.querySelector('body').dataset.visitId)

【讨论】:

非常感谢!尽管您的特定实现在我的用例中不起作用,但这个答案使我找到了一个非常干净且始终如一的解决方案。稍后我将概述我的具体方法。在body 上设置值的唯一问题是rails 在加载主体之前加载通道脚本/app/assets/javascripts/channels/visit.coffee。因此,该变量未设置。但是,我基本上按照您的建议做了,而且效果很好。 (就像我说的现在写出来) 你可以在 html 的末尾添加 javascript 到 【参考方案2】:

这里有几点需要考虑:

    脚本全部加载的顺序。 ruby 变量“实例化”的具体时间(可以访问它们的位置/时间)。 意识到可以在愚蠢的 CoffeeScript 中使用普通的 JavaScript。

也就是说 - 这是对我有用的解决方案:

文件和支持

除以下内容外,上述问题中使用的所有文件均未更改。要使其正常工作,您需要参考上面的文件和下面的文件以获取完整堆栈。

我的应用的主模板:app/views/layouts/application.html.erb 注意head标签内的行yield(:head_attributes)

<!DOCTYPE html>
<html>
  <head <%= yield(:head_attributes) %> >
    <title>Uvo Health</title>
    <%= action_cable_meta_tag %>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body  <%= yield(:body_attributes) %> >
    <%= render 'layouts/navbar' unless @hide_nav %>
    <%= render 'shared/flash_messages' %>
    <%= yield %>
    <%= yield :page_js %>
  </body>

</html>

我尝试在其中使用 actioncable 的页面的视图。就我而言,它是:app/views/visits/action_cable.html.erb - 大多数情况下可能是你的show.html.erbindex.html.erb 注意content_for

<%= content_for(:head_attributes) do %>data-visit-id="<%= @visit.id %>"<% end %>

<div class='container'>
  <%= render 'visits/visit_overview' %>
</div>

然后在我的访问频道/app/assets/javascripts/channels/visit.coffee

App.visit = App.cable.subscriptions.create  channel: 'VisitChannel', visit_id: document.querySelector('head').dataset.visitId ,
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    console.log data

  push: ->
    @perform 'push'

发生了什么:

    当页面加载时,视图会将data-visit-id 的数据属性添加到页面头部。 visit.coffee的频道从页首读取这个属性。 那么visit.coffee可以使用这个变量来订阅合适的频道。

其他解决方案不起作用,因为我试图以错误的“顺序”含义访问事物,在实例化之前加载变量。希望这对其他人有帮助。这个让我难倒了整整 5 个小时。 ?

【讨论】:

以上是关于订阅 Action Cable 频道时如何设置参数的主要内容,如果未能解决你的问题,请参考以下文章

多个订阅的 Action Cable 通知

Ruby on Rails 5 Action Cable:当前模型实例的流(基于 URL 的订阅)

Ruby on Rails:如何通过 ngrok 访问 Action Cable?

由 config.action_cable.url 定义的 URL 在被 Vue 前端使用时生成 404

Rails Action Cable:如何授权传入连接?

Capybara 不使用 action_cable