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