Reputation: 4439
I have a fairly standard Rails 5.2 app (follows pretty much all conventions) using yarn and webpacker, with stimulus version 1.1.1
in my package.json
and yarn.lock
file.
# 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"
}
}
From the StimulusJS Discourse page (https://discourse.stimulusjs.org/t/stimulusjs-and-turbolinks/669), starting with Stimulus 1.1
, stimulus controllers execute connect/initialize methods after DOM is ready with turbolinks.
However, the only way I could get the below controller to execute properly is to add the event handler to wait until the turbolinks:load
event fired.
If it's relevant info, I'm trying to use the jQuery Select2 plugin to create a custom select element.
# 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()
})
}
}
The HTML form:
<%= 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 %>
Am I missing something with setting up the stimulus controller with turbolinks?
Using the turbolinks:load
event handler, I can get the functionality I want, but from what I read on the Discourse forum, I shouldn't have to use the event handler.
Is it possible that my app has "cached" an older version of Stimulus, even though the package.json
says otherwise?
Upvotes: 3
Views: 4106
Reputation: 153
From my understanding, Stimulus has been designed to work in conjunction with Turbolinks. I would be surprised if you needed to wrap your code inside of the event handler for turbolinks:load
. It is possible that you may be looking to use the connect
event rather than the initialize
event because the initialize
event is fired off when the controller is first instantiated. The connect
event is fired off anytime the controller is connected to the DOM.
From the docs for the connection
lifecycle callback, it states:
A controller is connected to the document when both of the following conditions are true:
- its element is present in the document (i.e., a descendant of document.documentElement, the element)
- its identifier is present in the element’s data-controller attribute
When a controller becomes connected, Stimulus calls its connect() method.
Since you want to manipulate the DOM with jQuery Select2, the connect
lifecycle callback seems more appropriate to me. I will do some more research, but I would imagine that the initialize
event is fired whenever the code for the controller is loaded in the browser. If that is the case, then there is a possibility the initialize
event is getting fired before the part of the DOM tree that uses your controller (and the DOM element you're trying to query) has had a chance to render:
# app/javascript/packs/controllers/intake_customization_controller.js
import { Controller } from "stimulus";
export default class extends Controller {
static targets = [ "userIds" ]
connect() {
$(this.userIdsTarget).select2()
}
}
I was able to find an article says to use connect
rather than turbolinks:load
. This article is written by someone who has published a handful of Stimulus tutorials so he seems fairly reputable. I'm having trouble finding anything that goes into detail about the differences between initialize
and connect
other than that initialize
is fired once and connect
is fired every time it's attached to the DOM. In order for connect
event to be fired again, there needs to a disconnected
event fired in between
Upvotes: 4