Rails Action Cable и Turbolinks: избегайте множественных привязок

У меня есть код в моем application.html.haml, который решает, подписывать ли пользователя на данный канал или нет, в зависимости от некоторого атрибута пользователя.

Дело в том, что, учитывая, что у меня есть этот фрагмент кода в теле, когда я нажимаю ссылку, чтобы перенаправить ее на другую страницу, он снова подписывает пользователя вместо сохранения старого соединения, поэтому у меня есть receive() метод выполняется несколько раз.

Это мой код в 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()

активы / JavaScripts/ уведомление /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

При первой загрузке страницы, когда я транслирую сообщение на этот канал, консоль записывает "Полученные данные" только один раз. Я щелкаю ссылку, чтобы перенаправить ее на другую страницу, я транслирую новое сообщение на этот канал, и теперь консоль дважды регистрирует "Получено данных". И так до сих пор...

На самом деле, когда я бегу на консоли Chrome:

App.cable.subscriptions.subscriptions

Он возвращает несколько подписок на один и тот же канал (один раз за каждый раз, когда я нажимал на ссылку и перенаправлялся на другую страницу).

Примечание. В этом посте я не добавляю больше действий по настройке кабеля, потому что он работает нормально.

Как я могу избежать этого? Есть идеи?

2 ответа

Решение

Похоже, вы хотите только один экземпляр MyApp.AdminNotificationsтак что вам может понадобиться только добавить атрибут класса, чтобы указать, что класс был инициализирован:

window.MyApp.AdminNotifications = class AdminNotifications
  @init: ->
    unless @initialized
      App.cable.subscriptions.create "ProductsChannel",
        received: (data) ->
          console.log 'Data Received'
      @initialized = true

В будущем вы можете захотеть обернуть Action Cable API для управления своими собственными подписками.


Как правило, при работе с Turbolinks предпочтительно включать как можно больше материала в файл application.js, а не во встроенные скрипты (см. https://github.com/rails/rails/pull/23012). Я предполагаю, что вы использовали встроенный скрипт, чтобы вы могли его условно запустить (if current_user.is_admin?). Популярный подход заключается в использовании метатегов для передачи этих данных, поэтому в вашей голове:

<meta name="current-user-is-admin" content="true">

Тогда вы могли бы иметь:

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

И наконец, чтобы удалить AdminNotifications Код инициализации из макета включите его где-нибудь в ваш application.js:

document.addEventListener('turbolinks:load', ->
  window.MyApp.currentUser = new window.MyApp.User
  window.MyApp.AdminNotifications.init() if window.MyApp.currentUser.isAdmin
)

Надеюсь, это поможет!

Как насчет записи в голове вместо тела? Возможно, событие было зарегистрировано несколько раз через турболинк.

%html
  %head
    -# other stuff
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'

    - if current_user.is_admin?
      :coffee
        MyApp.AdminNotifications.init()

  %body
    -# other stuff
Другие вопросы по тегам