Ми хочемо зробити цей проєкт з відкритим кодом доступним для людей у всьому світі.

Допоможіть перекласти цей підручник вашою мовою!

5 листопада 2024 р.

FormData

У цьому розділі йдеться про відправлення 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)

Зазначимо дві особливості:

  1. Метод set видаляє поля з таким самим іменем, а append – ні. У цьому їхня єдина відмінність.
  2. Щоб надіслати файл, потрібно використовувати синтаксис з трьома аргументами, як третій вказується ім’я файлу, яке зазвичай, при <input type="file">, береться з файлової системи.

Інші методи:

  • formData.delete(name)
  • formData.get(name)
  • formData.has(name)

От і все!

Навчальна карта

Коментарі

прочитайте це, перш ніж коментувати…
  • Якщо у вас є пропозиції, щодо покращення підручника, будь ласка, створіть обговорення на GitHub або одразу створіть запит на злиття зі змінами.
  • Якщо ви не можете зрозуміти щось у статті, спробуйте покращити її, будь ласка.
  • Щоб вставити код, використовуйте тег <code>, для кількох рядків – обгорніть їх тегом <pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)