Stimulus — это легковесный JavaScript-фреймворк, предназначенный для усиления HTML с помощью поведения, не нарушая структуры документа. В задачах автосохранения и работы с черновиками Stimulus предоставляет удобный способ привязывать логику к элементам формы, обеспечивая реактивное и интуитивное взаимодействие с пользователем.
Основной строительный блок — контроллер. Контроллер связывает HTML-элемент с поведением JavaScript. Для автосохранения ключевым является связывание полей формы с методами, которые реагируют на события ввода.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "status"]
connect() {
this.loadDraft()
}
saveDraft() {
const data = this.inputTargets.map(target => target.value)
localStorage.setItem("draft", JSON.stringify(data))
this.statusTarget.textContent = "Черновик сохранён"
}
loadDraft() {
const data = JSON.parse(localStorage.getItem("draft") || "[]")
this.inputTargets.forEach((target, index) => {
target.value = data[index] || ""
})
}
}
В этом примере используются targets для полей формы
и индикатора состояния. Метод saveDraft сохраняет данные в
localStorage, а loadDraft подгружает их при
инициализации контроллера.
Targets позволяют обращаться к конкретным элементам внутри контроллера. Для автосохранения полезно создавать targets для каждого поля ввода, а также для элемента, отображающего статус сохранения.
Действия (actions) задаются через атрибуты HTML:
<form data-controller="draft">
<input data-draft-target="input" data-action="input->draft#saveDraft" type="text" name="title">
<textarea data-draft-target="input" data-action="input->draft#saveDraft"></textarea>
<div data-draft-target="status"></div>
</form>
Каждое изменение в input автоматически вызывает метод
saveDraft, обеспечивая реактивное
автосохранение.
Частое обращение к localStorage может быть избыточным. Использование debounce позволяет уменьшить нагрузку:
saveDraft = this.debounce(() => {
const data = this.inputTargets.map(target => target.value)
localStorage.setItem("draft", JSON.stringify(data))
this.statusTarget.textContent = "Черновик сохранён"
}, 500)
debounce(func, wait) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
Теперь метод saveDraft будет вызываться не чаще, чем раз
в полсекунды, что делает автосохранение более эффективным.
Stimulus позволяет создавать отдельные контроллеры для разных форм на одной странице, избегая конфликтов данных:
<form data-controller="draft" data-draft-id="post1">
...
</form>
<form data-controller="draft" data-draft-id="post2">
...
</form>
Контроллер можно модифицировать так, чтобы ключ для localStorage
зависел от data-draft-id:
saveDraft() {
const draftId = this.element.dataset.draftId
const data = this.inputTargets.map(target => target.value)
localStorage.setItem(`draft_${draftId}`, JSON.stringify(data))
}
Помимо сохранения, можно реализовать автоматическое восстановление
данных при загрузке страницы. Метод connect вызывается
автоматически, поэтому подгрузка черновика происходит сразу:
connect() {
this.loadDraft()
this.statusTarget.textContent = "Черновик восстановлен"
}
Можно добавить дополнительные проверки, например, удалять черновик после успешной отправки формы:
submitDraft(event) {
localStorage.removeItem(`draft_${this.element.dataset.draftId}`)
}
Stimulus превращает обычные формы в интерактивные элементы с автосохранением, сохраняя простоту и чистоту кода, что особенно важно при реализации черновиков и временного хранения данных.