StimulusJS с Turbolinks, нужно дождаться события turbolinks:load для выполнения контроллеров StimulusJS

У меня есть довольно стандартное приложение Rails 5.2 (соответствующее практически всем соглашениям), использующее пряжу и веб-упаковщик, с версией стимула 1.1.1 в моих файлах package.json и yarn.lock.

# package.json
{
  "name": "MY_APP_NAME",
  "private": true,
  "dependencies": {
    "@rails/webpacker": "^4.0.2",
    "coffeescript": "1.12.7",
    "stimulus": "^1.1.1"
  },
  "devDependencies": {
    "webpack-dev-server": "^3.2.1"
  }
}

Со страницы дискурса StimulusJS (https://discourse.stimulusjs.org/t/stimulusjs-and-turbolinks/669), начиная со Stimulus 1.1, контроллеры стимулов выполняют методы подключения/инициализации после того, как DOM будет готов с турболинками.

Однако единственный способ заставить приведенный ниже контроллер выполняться должным образом — это добавить обработчик событий, который будет ждать, пока не сработает событие turbolinks:load.

Если это актуальная информация, я пытаюсь использовать плагин jQuery Select2 для создания пользовательского элемента выбора.

# app/javascript/packs/controllers/intake_customization_controller.js
import { Controller } from "stimulus";

export default class extends Controller {
  static targets = [ "userIds" ]

  initialize() {
    // Code will not execute without this event handler wrapping it...
    $(document).on("turbolinks:load", ()=> {
      $(this.userIdsTarget).select2()
    })
  }
}

HTML-форма:

<%= form_with model: @account, url: settings_intake_customization_path, method: :put, id: "settings-intake_customization-form", data: { controller: "intake-customization" } do |form| %>
  <%= form.collection_select :user_ids, current_account.users, :id, :name, { include_blank: false }, { multiple: true, data: { target: "intake-customization.userIds" } } %>
<% end %>

Я что-то упустил при настройке контроллера стимулов с турболинками?

Используя обработчик событий turbolinks:load, я могу получить желаемую функциональность, но, судя по тому, что я прочитал на форуме Discourse, мне не нужно использовать обработчик событий.

Возможно ли, что мое приложение «кэшировало» более старую версию Stimulus, даже если package.json говорит об обратном?


person Dan L    schedule 09.07.2019    source источник


Ответы (1)


Насколько я понимаю, Stimulus был разработан для работы в сочетании с Turbolinks. Я был бы удивлен, если бы вам нужно было обернуть свой код внутри обработчика событий для turbolinks:load. Возможно, вы захотите использовать событие connect, а не событие initialize, потому что событие initialize срабатывает при первом создании экземпляра контроллера. Событие connect запускается каждый раз, когда контроллер подключается к DOM.

В документах для обратного вызова жизненного цикла connection говорится:

Контроллер подключается к документу, когда выполняются оба следующих условия:

  • его элемент присутствует в документе (т. е. потомок document.documentElement, элемент)
  • его идентификатор присутствует в атрибуте data-controller элемента

Когда контроллер подключается, Stimulus вызывает его метод connect().

Поскольку вы хотите манипулировать DOM с помощью jQuery Select2, обратный вызов жизненного цикла connect кажется мне более подходящим. Я проведу дополнительные исследования, но могу предположить, что событие initialize запускается всякий раз, когда код контроллера загружается в браузер. Если это так, то есть вероятность, что событие initialize запускается до того, как часть дерева DOM, использующая ваш контроллер (и элемент DOM, который вы пытаетесь запросить), успела отобразиться:

# app/javascript/packs/controllers/intake_customization_controller.js

import { Controller } from "stimulus";

export default class extends Controller {
  static targets = [ "userIds" ]

  connect() {
    $(this.userIdsTarget).select2()
  }
}

Мне удалось найти статью говорит использовать connect, а не turbolinks:load. Эта статья написана кем-то, кто опубликовал несколько пособий по Stimulus, так что он выглядит довольно уважаемым. . У меня возникли проблемы с поиском чего-либо, в котором подробно рассказывается о различиях между initialize и connect, кроме того, что initialize запускается один раз, а connect запускается каждый раз, когда он присоединяется к DOM. Чтобы событие connect было запущено снова, должно быть запущено событие disconnected между ними.

person Tyler Klose    schedule 11.07.2019
comment
Была такая же проблема, но с $(this.userIdsTarget).selectpicker(). Использование connect() - это путь. - person Sergio Gonzalez; 09.02.2021