Stimulus — это JavaScript-фреймворк, ориентированный на организацию поведения веб-страниц через контроллеры, которые связываются с DOM через data-атрибуты. Одной из часто встречающихся задач является реализация загрузки файлов с клиента на сервер. Stimulus предоставляет удобные механизмы для управления этим процессом, включая события, действия и состояние контроллера.
Создание контроллера начинается с наследования от класса
Controller:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "progress"]
connect() {
console.log("File upload controller connected")
}
}
Ключевые моменты:
static targets определяет элементы DOM, с которыми
контроллер будет взаимодействовать. Например, input для
<input type="file"> и progress для
индикатора загрузки.connect() вызывается при подключении контроллера
к DOM.Для обработки выбора файла используется действие change
на input:
<input type="file" data-controller="file-upload" data-file-upload-target="input" data-action="change->file-upload#handleFile">
<progress data-file-upload-target="progress" value="0" max="100"></progress>
Контроллер получает событие через метод:
handleFile(event) {
const file = event.target.files[0]
if (file) {
this.uploadFile(file)
}
}
Загрузка файлов осуществляется с помощью FormData и
fetch:
uploadFile(file) {
const formData = new FormData()
formData.append("file", file)
fetch("/upload", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => this.uploadSuccess(data))
.catch(error => this.uploadError(error))
}
Особенности:
FormData автоматически формирует тело запроса с нужными
заголовками.uploadSuccess и uploadError.Для отображения прогресса используется XMLHttpRequest
вместо fetch, так как стандартный fetch не
предоставляет прогресс:
uploadFile(file) {
const xhr = new XMLHttpRequest()
const formData = new FormData()
formData.append("file", file)
xhr.open("POST", "/upload", true)
xhr.upload.addEventListener("progress", event => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100
this.progressTarget.value = percent
}
})
xhr.addEventListener("load", () => this.uploadSuccess(JSON.parse(xhr.responseText)))
xhr.addEventListener("error", () => this.uploadError("Ошибка загрузки"))
xhr.send(formData)
}
Ключевые моменты:
xhr.upload.addEventListener("progress", …) позволяет
отслеживать прогресс передачи данных.lengthComputable предотвращает деление на
ноль и некорректные значения.Stimulus упрощает работу с множественными файлами через
input.multiple и массив FileList:
handleFile(event) {
const files = Array.from(event.target.files)
files.forEach(file => {
if (this.validateFile(file)) {
this.uploadFile(file)
}
})
}
validateFile(file) {
const allowedTypes = ["image/jpeg", "image/png", "application/pdf"]
const maxSize = 5 * 1024 * 1024 // 5 MB
if (!allowedTypes.includes(file.type)) return false
if (file.size > maxSize) return false
return true
}
Особенности:
Array.from() превращает FileList в
массив для удобного перебора.Stimulus позволяет легко управлять состоянием элементов:
uploadSuccess(data) {
this.progressTarget.value = 100
this.inputTarget.value = ""
console.log("Загрузка завершена:", data)
}
uploadError(error) {
this.progressTarget.value = 0
console.error("Ошибка загрузки:", error)
}
Принципы:
targets минимизирует необходимость
поиска элементов вручную.Stimulus удобно интегрируется с drag-and-drop:
<div data-controller="file-upload" data-action="drop->file-upload#handleDrop dragover->file-upload#allowDrop" class="drop-zone">
Перетащите файлы сюда
</div>
allowDrop(event) {
event.preventDefault()
}
handleDrop(event) {
event.preventDefault()
const files = Array.from(event.dataTransfer.files)
files.forEach(file => this.uploadFile(file))
}
Преимущества:
dragover и drop обеспечивают
интерактивность.Stimulus работает с любым сервером, поддерживающим HTTP-запросы:
direct_upload: true
интегрируется с stimulus-rails.multipart/form-data через
multer.request.FILES для получения загруженных
файлов.Фреймворк не накладывает ограничений на бэкенд, обеспечивая легковесное и декларативное управление фронтендом.
targets и actions создают декларативный
интерфейс, устраняя необходимость прямого доступа к элементам через
querySelector.Фокус на структурированности и реактивности делает Stimulus идеальным инструментом для реализации сложных сценариев загрузки файлов с минимальной связкой кода и DOM.