I have a code in my application.html.haml that decides whether to subscribe the user to a given channel or not, depending on some user attribute.
The thing is that, given I have this piece of code in the body, when I click a link to be redirected to another page, it subscribes the user again instead of keeping the old connection, so I have the receive()
method being executed multiple times.
This is my code in application.html.haml
:
%html
%head
-# other stuff
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
%body
-# other stuff
- if current_user.is_admin?
:coffee
MyApp.AdminNotifications.init()
assets/javascripts/notifications/admin_notifications.js.coffee
window.MyApp.AdminNotifications = class AdminNotifications
@init: ->
App.cable.subscriptions.create "ProductsChannel",
received: (data) ->
console.log 'Data Received' # This is being executed multiple times
The first time the page is loaded, when I broadcast a message to that channel, the console is logging "Data Received" just once. I click a link to be redirected to another page, I broadcast a new message to that channel, and now the console is logging "Data Received" twice. And so far so on...
In fact, when I run on the chrome's console:
App.cable.subscriptions.subscriptions
It returns multiple subscriptions to the same channel (once for every time I clicked on a link and got redirected to a different page).
Note: There is more action cable setup that I am not adding to this post because it's working fine.
How can I avoid that? Any ideas?
It looks like you only want a single instance of MyApp.AdminNotifications
, so you may only need to add a class attribute to flag that the class has been initialized:
window.MyApp.AdminNotifications = class AdminNotifications
@init: ->
unless @initialized
App.cable.subscriptions.create "ProductsChannel",
received: (data) ->
console.log 'Data Received'
@initialized = true
In the future you may want to wrap the Action Cable API to manage your own subscriptions.
As a general rule when working with Turbolinks, it is preferable to include as much stuff as possible in your application.js file rather than in inline scripts (see: https://github.com/rails/rails/pull/23012#issue-125970208). I'm guessing you used an inline script so you could conditionally run it (if current_user.is_admin?
). A popular approach is to use meta tags to convey this data, so in your head:
<meta name="current-user-is-admin" content="true">
Then you could have:
window.MyApp.User = class User
constructor: ->
@isAdmin = @getMeta('current-user-is-admin') == 'true'
getMeta: (name) ->
meta = document.querySelector("meta[name=#{name}]")
meta.getAttribute('content') if meta
And finally, to remove the AdminNotifications
init code from the layout include this somewhere in your application.js:
document.addEventListener('turbolinks:load', ->
window.MyApp.currentUser = new window.MyApp.User
window.MyApp.AdminNotifications.init() if window.MyApp.currentUser.isAdmin
)
Hope that helps!