A controller that fetches HTML every two seconds.
static values = { url: String, refreshInterval: Number }
connect() {
this.load()
if (this.hasRefreshIntervalValue) this.startRefreshing()
}
startRefreshing() {
this.timer = setInterval(() => this.load(), this.refreshIntervalValue)
}
The box on the right is that controller, wired to /messages.html. Requests tick along on the right. Now press remove the element, as a Turbo visit or a stream would, and keep watching the request log.
The element is gone. The timer isn't.
Nothing told the timer to stop. It keeps fetching into an element nobody can see, and after twenty Turbo visits you have twenty of them. This is the most common Stimulus bug and it has a one-method fix:
disconnect() {
clearInterval(this.timer)
}
Stimulus calls disconnect() whenever the element leaves the document: removed by a stream, replaced by a frame, swapped out by a Drive visit, or its data-controller attribute edited away. Whatever connect() acquired, disconnect() releases. Flip the switch, add the element back, remove it again.
Five callbacks. You'll use three.
| method | when |
|---|---|
initialize() | once, when the controller object is first created |
[name]TargetConnected(el) | each time a target element appears (before connect on first load) |
connect() | each time the element is in the document with the identifier on it |
[name]TargetDisconnected(el) | each time a target element leaves |
disconnect() | each time the element leaves, or the identifier is removed |
The rule that makes it all fit with Turbo: a Drive visit installs a new <body>, which disconnects every controller in the old one and connects every controller in the new one. Remove an element and put the same node back (a data-turbo-permanent element, say) and Stimulus reuses its controller instance, so connect() can run many times for one object. Keep it idempotent; keep initialize() for one-time setup.