Stimulus построен на концепции легковесных контроллеров, каждый из которых управляет отдельной частью DOM. В реальных приложениях часто возникает необходимость, чтобы контроллеры взаимодействовали друг с другом, обменивались данными или реагировали на события, происходящие вне их области ответственности. Stimulus предоставляет несколько эффективных способов для такой коммуникации.
Custom Events — основной инструмент для передачи информации между контроллерами. DOM позволяет создавать события с произвольными данными, которые могут быть перехвачены другими контроллерами.
this.element.dispatchEvent(new CustomEvent('user:updated', {
detail: { name: 'Иван', age: 30 },
bubbles: true
}))
detail содержит данные, которые передаются
слушателям.bubbles: true позволяет событию подниматься по DOM,
чтобы другие контроллеры могли его поймать.import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.addEventListener('user:updated', this.handleUserUpdate.bind(this))
}
handleUserUpdate(event) {
console.log(event.detail.name) // 'Иван'
console.log(event.detail.age) // 30
}
}
Ключевой момент: использование
bubbles: true позволяет контроллерам, находящимся выше в
DOM, реагировать на события, не создавая прямую зависимость между
ними.
application.getControllerForElementAndIdentifierStimulus предоставляет возможность напрямую получить контроллер по элементу и идентификатору. Это полезно, когда требуется вызвать методы другого контроллера.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
const otherController = this.application.getControllerForElementAndIdentifier(
document.querySelector("#profile"), "profile"
)
if (otherController) {
otherController.updateName("Иван")
}
}
}
Плюсы прямого вызова:
Минусы:
Stimulus.Application для глобальных событийДля сложных приложений удобно использовать объект
application как центральный диспетчер событий.
// в initialization.js
const application = Stimulus.Application.start()
application.register("user", UserController)
application.register("notifications", NotificationsController)
// В контроллере
this.application.dispatch('user:logged-in', { userId: 123 })
Другой контроллер может подписаться на эти события:
this.application.on('user:logged-in', (event) => {
console.log(event.userId)
})
Это позволяет:
targets и data-actionДля частого взаимодействия между близко расположенными элементами можно использовать targets и data-action.
<div data-controller="form">
<input data-form-target="name" data-action="input->form#updateName">
</div>
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["name"]
updateName() {
const value = this.nameTarget.value
this.element.dispatchEvent(new CustomEvent('name:changed', {
detail: value,
bubbles: true
}))
}
}
Другой контроллер может слушать событие name:changed и
выполнять свои действия. Это эффективно для локальной коммуникации между
компонентами на одной странице.
data-* атрибутыИногда контроллеры обмениваются состоянием через data-*
атрибуты элементов. Это позволяет связывать состояние с DOM без создания
дополнительных событий.
<div id="counter" data-controller="counter" data-counter-value="0"></div>
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { value: Number }
increment() {
this.value++
this.element.dataset.counterValue = this.value
}
}
Другой контроллер может прочитать data-counter-value для
синхронизации своего состояния.
getControllerForElementAndIdentifier — когда нужен
точный контроль и уверенность в наличии целевого контроллера.data-action — для локального
обмена между тесно связанными элементами.data-* атрибуты — для передачи
простого состояния через DOM без сложной логики событий.Комбинация этих подходов позволяет создавать гибкие, масштабируемые приложения на Stimulus, где контроллеры остаются независимыми, но эффективно обмениваются данными и событиями.