Rails针对特定页面的Actioncable

Das*_*kus 3 ruby-on-rails ruby-on-rails-5 actioncable

在我的项目中,我有一个模型categories,它有一个详细视图(显示).该视图包含应使用actioncable实时更新的数据.视图的内容/数据对于理解问题并不重要.

一个标准的设置,我只有一个类别与硬编码ID完美,但现在我想使它动态和页面特定,所以我不需要打开100个订阅,我mabye甚至不需要,如果我是不在类别详细信息页面上.

第一个问题:如何仅为当前页面/类别创建连接?

第二个问题:我如何获得当前类别的ID?

App.cable.subscriptions.create { channel: "RankingChannel", category_id: HOW_DO_I_GET_THIS}
Run Code Online (Sandbox Code Playgroud)

我发现的唯一的事情就是这个,但它不起作用:https: //stackoverflow.com/a/36529282/5724835

小智 6

通道似乎不是特定于页面的(就像控制器不是特定于页面的那样),但是您可以调整通道的范围(正如您尝试的那样),以便订阅仅接收给定类别的消息.


您可以通过在布局文件的头部放置这样的内容,有选择地在页面上包含通道的js文件:

<%= javascript_include_tag 'channels/'+params[:controller] %>
Run Code Online (Sandbox Code Playgroud)

而不是//= require_tree ./channels在你的cables.js文件中.

但这样做的缺点是要求您在config\initializers\assets.rb初始化程序中的预编译数组中包含js文件并重新启动服务器:

Rails.application.config.assets.precompile += %w( channels/categories.js )
Run Code Online (Sandbox Code Playgroud)

通道定义是您使用stream_for模型对流进行范围调整的位置,这使得连接ID唯一,例如,categories:Z2lkOi8vcmFpbHMtc3RaaaRlci9Vc2VyLzI而不仅仅是categories.

应用程序/信道/ ranking_channel.rb

class RankingChannel < ApplicationCable::Channel
  def subscribed
    category = Category.find(params[:category_id])
    stream_for category
  end

  ...

end
Run Code Online (Sandbox Code Playgroud)

您将category_id客户端页面发送到服务器.在这里,我们通过DOM中元素的data-category-id属性#category(使用jQuery语法,但可以很容易地转换为直接js)来完成它.

应用程序/资产/ Javascript角/信道/ categories.js

$( function() {
  App.categories = App.cable.subscriptions.create(
    {
      channel: "RankingChannel",
      category_id: $("#category").data('category-id')
    },{
      received: function(data) {
        ...
      }
    }
  );
});
Run Code Online (Sandbox Code Playgroud)

因此,在您的视图中,您需要在生成页面时包含类别ID:

应用程序/视图/类别/ show.html.erb

<div id="category" data-category-id="<%= @category.id %>">
  <div class="title">My Category</div>

  ...

</div>
Run Code Online (Sandbox Code Playgroud)


bon*_*ndo 5

好吧,对于 Rails 6,我解决了类似于上面 @YaEvan 的答案。

我对所有*_channel.js文件设置了一个条件:

import consumer from "./consumer"

document.addEventListener('turbolinks:load', function() {
  // Because I was going to work with this element anyway, I used it to make the condition.
  var body = document.getElementById("news_body");
  if(body) {
    consumer.subscriptions.create("NewsChannel", {
      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) {
        // Called when there's incoming data on the websocket for this channel
      }
    });
  }
});
Run Code Online (Sandbox Code Playgroud)

这是一个变通办法。我没有找到正确方法的答案。所以如果你发现了,请告诉我!