Перейти к содержимому
v2.5.0
English
Русский
Документация

Формы и валидация

Поля Vega UI — нативные элементы формы, поэтому обычная <form> отправляет, проверяет и сбрасывает их без дополнительного состояния.

Работу делают нативные поля

Каждая часть поля рендерит настоящий элемент: TextFieldInput, PasswordFieldInput, NumberFieldInput, PhoneFieldInput, DateFieldInput и TimeFieldInput — это <input>, TextArea — <textarea>, Radio — <input type="radio">, а CheckboxInput, SwitchInput, SegmentedControlItemInput и SliderThumbInput — поля внутри своих компонентов. PinField хранит код в одном визуально скрытом поле за своими ячейками.

Поэтому формы получают поведение браузера бесплатно:

  • name, value, required, pattern, min, max, minLength и autoComplete ставятся на часть-поле, ровно как на нативный элемент
  • new FormData(form) читает значения, а отправка без preventDefault() передаёт их, как любая HTML-форма
  • у Button нет type по умолчанию, поэтому внутри формы она отправляет её, как нативная <button>; передайте type='button' кнопкам, которые не должны этого делать
  • автозаполнение браузера, менеджеры паролей и атрибут form работают с полями как обычно
profile-form-submit.tsx · 39 строк
import { Button, Label, Text, TextField, TextFieldInput } from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';

export default function ProfileFormSubmit() {
  const id = useId();
  const [saved, setSaved] = useState('');

  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    setSaved(`${data.get('fullName')} <${data.get('email')}>`);
  };

Что отправляет каждое поле

Нативное поле отправляет текст, который показывает. Для большинства полей это и есть значение; несколько компонентов держат на корне другое значение:

КомпонентОтправляется при name на полеЗначение на корне
TextField, TextArea, PasswordFieldвведённый текст—
NumberFieldотформатированный текст, например 2 500value / defaultValue числом, NaN для пустого поля
DateField, DateRangeField, DateTimeField, TimeFieldвведённый текст в format, например 15.01.2026ISO value / defaultValue, см. Даты и календарные системы
Checkbox, Radio, Switch, SegmentedControlvalue каждого отмеченного поля—
Sliderзначение каждого бегунка; name на корне применяется ко всем бегункамvalue / defaultValue массивом
Selectвыбранное значение через SelectHiddenSelect с namevalue / defaultValue

Если сервер ждёт значение корня, а не показанный текст, повторите его в скрытом поле из onChangeValue. Так пример DateField отправляет ISO-день. У Listbox нет части для формы: рендерите по скрытому полю на каждое выбранное значение, как в Listbox в форме.

native-form-participation.tsx · 68 строк
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

Валидация ограничений

required, pattern, type='email', min и max на частях-полях блокируют отправку и заполняют input.validity, как у нативных полей. Браузер показывает своё всплывающее сообщение, если вы не возьмёте это на себя:

  • слушайте onInvalid на поле и вызывайте event.preventDefault(), чтобы скрыть подсказку браузера, а затем читайте event.currentTarget.validity, чтобы выбрать своё сообщение
  • или поставьте noValidate на форму и проверяйте значения сами в onSubmit

Вид ошибки включается явно и никогда не выводится из валидности. Передайте error корню поля (TextField, TextArea, NumberField, PinField) и HelperText, чтобы переключить их на цвета ошибки. Библиотека не ставит ARIA за вас: задайте aria-invalid на поле и укажите в aria-describedby тот HelperText, который несёт сообщение.

native-validity-message.tsx · 49 строк
import { Button, HelperText, Label, TextField, TextFieldInput } from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';

const getMessage = (input: HTMLInputElement) => {
  if (input.validity.valueMissing) return 'Enter a postal code.';
  if (input.validity.patternMismatch) return 'Use 5 digits, for example 10115.';
  return '';
};

export default function NativeValidityMessage() {
  const id = useId();
  const [error, setError] = useState('');

Элементы без нативного ограничения, например Select без выбора, проверяются в onSubmit: храните ошибку в состоянии, ставьте aria-invalid на SelectCombobox и переводите на него фокус. См. обязательный выбор в Select.

Отключение группы

Fieldset рендерит нативный <fieldset>, поэтому disabled на нём отключает все элементы внутри по правилам браузера: поля перестают принимать фокус и ввод, а отключённые элементы не попадают в FormData. Объясните причину рядом с группой через HelperText, а не отключайте её молча.

disabled-by-fieldset.tsx · 60 строк
import { useState } from 'react';
import {
  Checkbox,
  CheckboxCheckedIcon,
  CheckboxIndeterminateIcon,
  CheckboxIndicator,
  CheckboxInput,
  Fieldset,
  FieldsetLegend,
  HelperText,
} from '@vega-ui/react';

Сброс

<button type='reset'> возвращает каждое неконтролируемое поле к значению по умолчанию:

  • нативные части возвращаются к defaultValue или defaultChecked
  • NumberField, PhoneField, PinField, DateField, DateRangeField, DateTimeField, TimeField и Slider восстанавливают defaultValue своего корня, в том числе для формы, связанной атрибутом form
  • контролируемое поле сохраняет свой value, потому что им владеет родитель: сбросьте это состояние в onReset формы

В onReset же удобно очищать результаты отправки и сообщения об ошибках, которые хранятся в состоянии.

Чек-лист

  • Дайте name каждому отправляемому полю; поле без него не попадает в FormData.
  • Держите правила валидации на частях-полях, чтобы браузер, автозаполнение и сервер видели одни и те же ограничения.
  • Связывайте aria-invalid и aria-describedby сами, а error передавайте только как визуальное состояние.
  • Отключайте группу через Fieldset с disabled, а не через disabled на каждом элементе.
  • Проверяйте те же правила на сервере: валидация на клиенте — удобство, а не гарантия.