У цьому розділі йдеться про відправлення HTML-форм: з файлами та без, з додатковими полями й так далі.
Об’єкти FormData допоможуть нам із цим. Як ви, напевно, здогадалися за назвою, це об’єкт, що представляє дані HTML форми.
Конструктор:
let formData = new FormData([form]); Якщо передати в конструктор елемент HTML-форми form, то об’єкт, що створюється, автоматично прочитає з неї поля.
Його особливість полягає в тому, що методи для роботи з мережею, наприклад, fetch, дозволяють вказати об’єкт FormData у властивості тіла запиту body.
Тобто для сервера це виглядає як звичайне надсилання форми.
Надсилання простої форми
Давайте спочатку надішлемо просту форму.
Як ви бачите, код дуже компактний:
<form id="formElem"> <input type="text" name="name" value="John"> <input type="text" name="surname" value="Smith"> <input type="submit"> </form> <script> formElem.onsubmit = async (e) => { e.preventDefault(); let response = await fetch('/article/formdata/post/user', { method: 'POST', body: new FormData(formElem) }); let result = await response.json(); alert(result.message); }; </script> У цьому прикладі серверний код не представлений, він за рамками цієї статті, він приймає POST-запит із даними форми та відповідає повідомленням «Користувач збережений».
Методи об’єкта FormData
За допомогою наведених нижче методів ми можемо змінювати поля в об’єкті FormData:
formData.append(name, value)– додає до об’єкта поле з іменемnameі значеннямvalue,formData.append(name, blob, fileName)– додає поле так, ніби це<input type="file">, третій аргументfileNameвстановлює ім’я файлу (не ім’я поля форми), ніби це ім’я з файлової системи користувача,formData.delete(name)– видаляє поле по заданомуname,formData.get(name)– дістає значення поля по заданомуname,formData.has(name)– перевіряє чи існує поле по заданомуname, повертаєtrue, інакшеfalse
Технічно форма може мати багато полів з тим самим ім’ям name, тому кілька викликів append додадуть кілька полів з однаковими іменами.
Ще існує метод set, його синтаксис такий самий, як у append. Різниця в тому, що .set видаляє всі наявні поля з ім’ям name і тільки потім додає нове. Тобто цей метод гарантує, що існуватиме лише одне поле з ім’ям name, у всьому іншому він аналогічний .append:
formData.set(name, value),formData.set(name, blob, fileName).
Поля об’єкта formData можна перебирати, використовуючи цикл for..of:
let formData = new FormData(); formData.append('key1', 'value1'); formData.append('key2', 'value2'); // Список пар ключ/значення for(let [name, value] of formData) { alert(`${name} = ${value}`); // key1 = value1, then key2 = value2 } Надсилання форми з файлом
Об’єкти FormData завжди посилаються із заголовком Content-Type: multipart/form-data, цей спосіб кодування дозволяє надсилати файли. Таким чином, поля <input type="file"> відправляються так само, як і решта полів форми.
Приклад такої форми:
<form id="formElem"> <input type="text" name="firstName" value="John"> Picture: <input type="file" name="picture" accept="image/*"> <input type="submit"> </form> <script> formElem.onsubmit = async (e) => { e.preventDefault(); let response = await fetch('/article/formdata/post/user-avatar', { method: 'POST', body: new FormData(formElem) }); let result = await response.json(); alert(result.message); }; </script> Надсилання форми з даними Blob
Раніше у главі Fetch ми бачили, що дуже легко відправити динамічно згенеровані бінарні дані у форматі Blob. Ми можемо явно передати їх до параметра body запиту fetch.
Але на практиці буває зручніше відправляти зображення не окремо, а у складі форми, додавши додаткові поля для імені та інші метадані.
Крім того, сервери часто налаштовані на приймання саме форм, а не просто бінарних даних.
У прикладі нижче надсилається зображення з <canvas> і ще кілька полів, як форма, використовуючи FormData:
<body style="margin:0"> <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas> <input type="button" value="Submit" onclick="submit()"> <script> canvasElem.onmousemove = function(e) { let ctx = canvasElem.getContext('2d'); ctx.lineTo(e.clientX, e.clientY); ctx.stroke(); }; async function submit() { let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); let formData = new FormData(); formData.append("firstName", "John"); formData.append("image", imageBlob, "image.png"); let response = await fetch('/article/formdata/post/image-form', { method: 'POST', body: formData }); let result = await response.json(); alert(result.message); } </script> </body> Будь ласка, зверніть увагу на те, як додається зображення Blob:
formData.append("image", imageBlob, "image.png"); Це як би у формі був елемент <input type="file" name="image"> і користувач прикріпив би файл з ім’ям "image.png" (3й аргумент) та даними imageBlob (2й аргумент) зі своєї файлової системи.
Сервер прочитає і дані і файл, так само, якби це була звичайна відправка форми.
Підсумки
Об’єкти FormData використовуються, щоб взяти дані з HTML-форми та відправити їх за допомогою fetch або іншого методу для роботи з мережею.
Ми можемо створити такий об’єкт з даними, передавши в конструктор HTML-форму – new FormData(form), або ж можна створити об’єкт взагалі без форми і потім додати до нього поля за допомогою методів:
formData.append(name, value)formData.append(name, blob, fileName)formData.set(name, value)formData.set(name, blob, fileName)
Зазначимо дві особливості:
- Метод
setвидаляє поля з таким самим іменем, аappend– ні. У цьому їхня єдина відмінність. - Щоб надіслати файл, потрібно використовувати синтаксис з трьома аргументами, як третій вказується ім’я файлу, яке зазвичай, при
<input type="file">, береться з файлової системи.
Інші методи:
formData.delete(name)formData.get(name)formData.has(name)
От і все!
Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)