Stimulus: events aren't available via connect()

Created on 21 Jan 2019  路  2Comments  路  Source: hotwired/stimulus

Hello

I have a page that shows a form, which is mapped to a Stimulus controller, <form data-controller="form">.

Initial action on pageload, should fire a chain of events

When the page is displayed, I want to have the first field of the form focused, so the user can start typing. I do this in connect() using jQuery - old habits die hard :)

connect() {
   $('input#first').focus();
}

In the HTML, all input fields have an Stimulus action data-action="focus->form#addActiveClass blur->form#removeActiveClass".

<form data-controller="form">
  <input data-action="focus->form#addActiveClass blur->form#removeActiveClass" name="one">
  <input data-action="focus->form#addActiveClass blur->form#removeActiveClass" name="two">
  <input data-action="focus->form#addActiveClass blur->form#removeActiveClass" name="three">
</form>

Events don't fire on initial pageload

When doing an initial pageload, these events data-action don't fire when the connect() method calls triggers the focus event.

The field is focused, like the line in my connect() method specifies, but the addActionClass() method of my form controller is not called.

Events do fire if cached turbolinks page

Using turbolinks however, if I navigate away from the page with the form, and back to it, then the events do fire. The addActionClass() method of my form controller is called.

Thoughts

My guess is this is because the events are _already_ attached to the field when turbolinks loads the page from cache, where as on initial pageload, the controller's connect() method fires before it's wired-up all the data-action linkages.

The reference docs say:

Connection

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 <html> element)
  • its identifier is present in the element鈥檚 data-controller attribute

When a controller becomes connected, Stimulus calls its connect() method.

Is this an issue with how I'm trying to use connect(), and what should I be doing different?

Or, should connect() be waiting until everything's wired up? (i.e. a third bullet-point is needed in the reference docs saying:

  • all data-action references on descendants are wired-up and ready for triggering

Most helpful comment

Great catch. Swapping the order of these two lines in Application#start fixes the problem:
https://github.com/stimulusjs/stimulus/blob/7f58b5597292e88423c3fa9ccd0e6f25ffc83192/packages/%40stimulus/core/src/application.ts#L29-L30

I believe this is only an issue on the initial page load because we start the dispatcher (and thus add event listeners for all actions already on the page) _after_ we start the router (which initializes controllers for elements already on the page).

All 2 comments

I know this is crazy, but if I wrap my call in connect() in the quickest of timeouts... it works.

connect() {
  setTimeout(function() {
    $('input#first').focus();
  }, 1);
}

Great catch. Swapping the order of these two lines in Application#start fixes the problem:
https://github.com/stimulusjs/stimulus/blob/7f58b5597292e88423c3fa9ccd0e6f25ffc83192/packages/%40stimulus/core/src/application.ts#L29-L30

I believe this is only an issue on the initial page load because we start the dispatcher (and thus add event listeners for all actions already on the page) _after_ we start the router (which initializes controllers for elements already on the page).

Was this page helpful?
0 / 5 - 0 ratings