Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Actioncable for specific pages

In my project I have a model categories, which has a detail view(show). The view contains data that should be updated in realtime with actioncable. The content/data of the view is not important to understand the problem.

A standart setup where I have just a single category with hardcoded id works perfectly, but now I want to make it dynamic and page specific, so that I don't need to open 100 subscriptions that I mabye do not even need if I'm not on a categories detail page.

First problem: how can I create connections for only the current page/category?

Second problem: how can I get the id of the current category?

App.cable.subscriptions.create { channel: "RankingChannel", category_id: HOW_DO_I_GET_THIS}

The only thing I found was this, but it did not work: https://stackoverflow.com/a/36529282/5724835

like image 767
Daskus Avatar asked Sep 20 '16 15:09

Daskus


1 Answers

Channels don't seem to be designed to be page specific (just as controllers are not page specific), but you can scope your channel (as you are trying to do) so that the subscription only receives messages for the given category.


You can selectively include the channel's js file on a page by putting something like this in the head of the layout file:

<%= javascript_include_tag 'channels/'+params[:controller] %>

rather than doing //= require_tree ./channels in your cables.js file.

but this has the disadvantage of requiring you to include the js file in the precompile array in your config\initializers\assets.rb initializer and restarting your server:

Rails.application.config.assets.precompile += %w( channels/categories.js )

The channel definition is where you scope the stream, using stream_for with a model, which makes the connection id unique, e.g., categories:Z2lkOi8vcmFpbHMtc3RaaaRlci9Vc2VyLzI rather than just categories.

app/channels/ranking_channel.rb

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

  ...

end

You send the category_id from the client page to the server. Here, we're doing it via a data-category-id attribute on a #category element in the DOM (using jQuery syntax, but could be converted to straight js pretty easily).

app/assets/javascripts/channels/categories.js

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

So in your view you would need to include the category id when the page is generated:

app/views/categories/show.html.erb

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

  ...

</div>
like image 182
clairity Avatar answered Nov 14 '22 22:11

clairity