The most common suggestion is to add a Stimulus controller in the rendered element and use the connect() method to do whatever you want. But this is polluting the new element's controller with logic that doesn't belong to it.
I come out with a compromised solution. Yes, I add a Stimulus controller in the rendered element, but this Stimulus controller is very lean and generic, so it can be reusable, and the logic that reacts to the event is in another specialized controller.
This is the generic solution I am using:
I have a reusable controller that triggers a CustomEvent.
The value of the CustomEvent is configurable via data-value attribute:
// app/javascript/controllers/i_am_here_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = {
eventName: String
}
connect() {
const trigger = new CustomEvent(this.eventNameValue);
window.dispatchEvent(trigger);
}
}
In the element that is rendered with the TurboStream I connect this controller:
<div
data-controller="i-am-here"
data-i-am-here-event-name-value="my-element-rendered"
>
/* edited */
</div>
Now I just have to listen to this event in another controller
which is responsible to react to when the content is loaded:
// app/javascript/controllers/reaction_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
window.addEventListener("my-element-rendered", this.react.bind(this));
}
react() {
console.log("Here I can react");
}
}