"turbo:load" event is not firing from Stimulus controller

Hello, I’m trying to call two methods from within a Stimulus controller by listening to the “turbo:load” event, but the event is never fired, therefore the other two methods never get triggered. This is how it looks:
form_controller.js

  connect() {
    document.addEventListener('turbo:load', function() {
      console.log('Turbo Loaded');
      this.setRegionQuestionElements(this.questions_id)
      this.checkRegionQuestions()
    });
  }

Could it be that the “turbo:load” event is being fired before this Stimulus controller have even loaded?
Thanks!

I’m wondering do you even need turbo:load listener. Can you just call the functions inside connect method without turbo:load listener?

1 Like

That’s an excellent point. By the time the Stimulus controller runs, that turbo:load event has already fired and disappeared over the horizon.

By definition, a Stimulus controller is invoked whenever an element that declares it (and based on the example you shared, I’m guessing that would be a <form data-controller="form">) appears in the DOM or changes in some way. That element could be part of the page at initial load, or hoisted into the page through some replacement scheme, and the Stimulus machinery will notice it and register it and run its connect method.

This is all based on Mutation Observers, not Event Observers, so it doesn’t matter if an event has fired or not. If you want to step back a ways, you could imagine that the Mutation Observer was initially wired up by a turbo:load Event Observer, but I’m pretty sure even that isn’t precisely true.

My limited understanding of Mutation Observers is that they initialize very early in the page’s life-cycle, and then watch for changes. When Turbo replaces the page body or a portion thereof, the DOM is updated, not unloaded and replaced anew. The Mutation Observer that checks for new Stimulus controllers to register or unregister remains in place, outside of the changed portions of the DOM, like some sort of eternal Watcher from the comics. From that vantage point, it notices those changes and acts accordingly.

Walter

1 Like

Right! That’s what I thought, but here’s some more context:
The functions inside my connect method are interacting with DOM components that are actually connected to a different Stimulus controller (let’s call it component_controller.js), but if the form_controller.js connects before the component controller has finished connecting to each of its component instances, then by the time the form_controller.js connects and run its methods it will probably not find some of these elements/components in the DOM since some of them haven’t finished connecting to the component controller.

All of that lead me to give a try using turbo:load for this purpose, to see if I could just call these form controller functions when the turbo:load fires (and the page has fully loaded).

But thanks to @walterdavis response now I know that it wouldn’t have worked anyways since the turbo:load event is fired before these Stimulus controllers are connected.

However, if you have any other ideas on how to run this form_controller.js functions only once the component_controller.js and its components have fully loaded, it would be great!

Thank you so much guys!
Roy

If you are eager loading stimulus controllers, you just need to make sure your form controllers comes last as stimulus processes controllers in order.

- data-controllers="form component" data-action="form:connect=>component#myForm"
+ data-controllers="component form" data-action="form:connect=>component#myForm"

If you are lazy loading the stimulus controllers, then it’s a bit trickier.