I’m in the middle of converting a Rails 6 app that didn’t use Turbolinks to Rails 7 using both Hotwire and Turbo. For context, I have a link that goes to the Rails controller’s new action, and then redirects to an edit action.
On the page that the user is redirected to, I have a div with an attached WizardController:
<div data-controller="wizard">...</div>
The WizardController.connect
method depends on the baseURI of the document, as I’m testing to see if the URI string contains “new”, e.g.:
if (document.baseURI.match("/new"))
return
Before Turbo, all this worked fine, as the baseURI of the document had already changed from new
to edit
. After switching to Turbo, though, it’s not working. The problem is that by the time the WizardController connects, Turbo has not changed the current baseURI from new
to edit
. So it always finds “new” in the string, exiting the function when not intended.
I’ve gone through a few different possibilities to make this work:
Attempt #1: Using setTimeout
in the WizardController.connect to run a different method with the same code that connect
previously contained:
connect() {
setTimeout(() => {
this.setup()
}, 500)
}
It works, because it forces the code run after Turbo has changed the baseURI, but just feels like a hack, plus it by definition makes the page less performant.
Attempt #2. Removing the controller from the div, and attaching it after turbo:load
, which allows me to use the Stimulus connect method as expected.
<div data-wizard="true" data-controller="">...</div>
document.addEventListener("turbo:load", () => {
let wizardYield = document.querySelector("[data-wizard='true']")
if (wizardYield)
wizardYield.dataset.controller = "wizard"
})
This works, but doesn’t “feel right” and makes it hard to easily understand what’s going on. I don’t like having a separate document event listener for the sole purpose of connecting this controller just to make sure Turbo’s loaded.
Attempt #3. Adding two actions to the wizard’s element and using a custom method rather than connect:
<div data-controller="wizard" action="turbo:load@document->wizard#setup load@window->wizard#setup">...</div>
This also works, and is my favorite of the three. But I’m still not wild about it, because it requires using a custom method in the Stimulus controller rather than the standard connect
. And I don’t love having that separate load@window
action. But without it, the setup method is only run on a Turbo visit, and is not run when the user does a manual page refresh. (I don’t understand why that’s the case - isn’t turbo:load
run on a page refresh?)
Is there a ‘better’ or ‘cleaner’ way to do this? It seems like a complicated solution to something that has to have come up before. Surely others have needed to ensure that turbo:load
has already run by the time Stimulus connect
is being executed?