В Stimulus выходы (outputs) — это
механизм для передачи событий от одного контроллера к другому,
позволяющий организовать взаимодействие между контроллерами без жёсткой
зависимости. Выходы работают как объявленные события,
которые контроллер может испускать, а другой контроллер — прослушивать и
реагировать на них.
Выходы повышают модульность и переиспользуемость кода, поскольку контроллеры могут общаться через декларативные события, не зная друг о друге напрямую.
Выход объявляется в статическом свойстве static outputs
контроллера. Формат:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static outputs = ["selected"]
connect() {
console.log("Контроллер подключен")
}
}
Здесь контроллер объявляет выход selected. Это значит,
что он может испускать событие selected,
которое может быть перехвачено другим контроллером.
Чтобы испустить событие, используется метод
this.dispatch(outputName, options):
this.dispatch("selected", { detail: { id: 42 } })
Пояснения:
"selected" — имя выхода, должно соответствовать
объявленному в static outputs.detail — объект с любыми данными, которые необходимо
передать.Контроллер, который должен реагировать на выход, может использовать декларативное связывание через data-атрибуты. Пример:
Контроллер item может иметь метод select,
который вызывает this.dispatch("selected"). Контроллер
list слушает этот выход через атрибут
data-list-selected-output="item" и автоматически вызывает
метод item с данными.
Прямое связывание через метод в контроллере:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["outputContainer"]
connect() {
this.element.addEventListener("item:selected", this.handleItemSelected.bind(this))
}
handleItemSelected(event) {
console.log("Получены данные:", event.detail)
}
}
Ключевой момент: имя события формируется как
, что позволяет
точно идентифицировать источник события.
Выходы удобны для передачи структурированных данных между контроллерами:
this.dispatch("selected", { detail: { id: 123, name: "Тестовый элемент" } })
В контроллере-получателе можно обработать эти данные напрямую:
handleItemSelected(event) {
const { id, name } = event.detail
console.log(`Выбран элемент ${id}: ${name}`)
}
Это позволяет строить сложные интерфейсы с динамическим реагированием на действия пользователя без жёсткой связи между компонентами.
Событие из дочернего контроллера для родителя
Например, контроллер карточки товара отправляет событие
selected, родительский контроллер списка получает его и
обновляет панель с информацией.
Событие между соседними контроллерами Выход всплывает по DOM, и любой контроллер выше в иерархии может перехватить его, что устраняет необходимость в прямых ссылках на соседей.
Обработка нескольких событий Можно объявлять несколько выходов в одном контроллере:
static outputs = ["selected", "deleted", "updated"]
Каждый выход можно слушать отдельно и выполнять разные действия.
// product_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static outputs = ["addToCart"]
addToCart() {
const product = { id: 1, name: "Кофе", price: 150 }
this.dispatch("addToCart", { detail: product })
}
}
// cart_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.addEventListener("product:addToCart", this.handleAddToCart.bind(this))
}
handleAddToCart(event) {
console.log("Добавлен продукт:", event.detail)
}
}
В этом примере контроллер продукта не знает о контроллере
корзины, а корзина реагирует на событие addToCart,
обеспечивая чистое межконтроллерное взаимодействие.