Vue
Формы и v-model
Форма регистрации собирает имя, email, согласие на рассылку, выбранный тариф и страну. Без двусторонней привязки на каждое поле пришлось бы вручную писать :value и @input, а потом отдельно приводить строку количества к числу и обрезать случайные пробелы в email. v-model сворачивает связь значения и события ввода в один атрибут, а модификаторы .number и .trim убирают ручное приведение типов и чистку прямо в шаблоне. Один и тот же синтаксис работает на input, чекбоксе, радио, select и даже на собственном компоненте поля.
- Поля профиля: имя и email через v-model на текстовых input, обрезка пробелов через .trim
- Согласие на условия через v-model на чекбоксе: булево значение без ручного чтения checked
- Выбор тарифа через v-model на группе radio, выбор страны через v-model на select
- Поле количества с .number, чтобы введённое значение стало числом, а не строкой, для арифметики корзины
Предварительные знания
- Шаблонный синтаксис и привязка атрибутов через v-bind
- Обработка событий и понимание события input
- Реактивное состояние через ref
v-model на текстовом поле
На текстовом input v-model связывает значение поля с реактивной переменной в обе стороны: ввод обновляет переменную, а изменение переменной обновляет поле. Это сахар над двумя вещами сразу: привязкой :value и слушателем @input. Вместо двух атрибутов пишут один v-model.
Двусторонность означает один источник правды: переменная name. Поле всегда отражает её значение, а ввод всегда пишет в неё. Расхождения между тем, что в поле, и тем, что в состоянии, не возникает.
На textarea используют v-model на самом теге, а не интерполяцию внутри (`{{ }}` внутри textarea не работает для привязки значения). На textarea v-model работает так же, как на однострочном input.
Во что разворачивается v-model="name" на обычном текстовом input?
Чекбоксы, радио и select
v-model подстраивается под тип поля. На одиночном чекбоксе он связывает булево значение. Если несколько чекбоксов привязаны к одному массиву, в массив попадают значения отмеченных. На группе радио v-model хранит выбранное значение. На select - значение выбранного option, а с атрибутом multiple - массив значений.
| Тип поля | Тип значения в v-model | Слушаемое событие |
|---|---|---|
| Одиночный чекбокс | boolean | change |
| Группа чекбоксов на массив | array значений | change |
| Группа radio | значение выбранного | change |
| select | значение option | change |
Для чекбокса можно задать собственные значения через true-value и false-value, например 'yes' и 'no' вместо булева. Тогда v-model хранит не true/false, а эти строки, что удобно при отправке на API с нестандартным форматом.
Что попадёт в переменную, если несколько чекбоксов с разными value привязаны к одному массиву через v-model?
Модификаторы .lazy, .number, .trim
v-model принимает встроенные модификаторы, убирающие ручную обработку значения. .lazy синхронизирует по событию change (после потери фокуса), а не на каждый символ. .number приводит введённое к числу - иначе значение input всегда строка. .trim обрезает пробелы по краям, что особенно важно для email и логинов.
Без .number значение из input всегда строка, даже при type="number". Арифметика вроде price * qty при строке даст конкатенацию или NaN. .number решает это, приводя ввод к числу при синхронизации.
Модификаторы комбинируются: v-model.lazy.trim обрежет пробелы и обновит значение по change. Это удобно для полей, где валидация и запись нужны после ввода, а не на каждый символ.
Поле количества товара привязано через v-model без модификаторов. Почему выражение price * qty даёт неверный результат?
v-model на компоненте через defineModel
Своё поле, например стилизованный input с лейблом и валидацией, тоже должно поддерживать v-model. В Vue 3.4+ это делает макрос defineModel: он создаёт реактивный ref, чтение которого отдаёт текущее значение, а запись испускает событие наверх. Под капотом это всё та же связка props (modelValue) и emit (update:modelValue), но без ручного объявления.
Для нескольких полей в одном компоненте объявляют именованные модели: defineModel('start') и defineModel('end'), а родитель привязывает их как v-model:start и v-model:end. Это покрывает компоненты вроде выбора диапазона дат.
Что создаёт макрос defineModel внутри пользовательского компонента?
Связь с другими темами
Формы соединяют привязку и события и доходят до собственных компонентов полей:
- Обработка событий — v-model под капотом слушает событие input и пишет значение обратно, что разбиралось в событиях
- Связь компонентов — v-model на компоненте строится на props и emit, рассмотренных в уроке про коммуникацию
Итог
- v-model - двусторонняя привязка: на input это сахар над :value и @input, на чекбоксе над :checked и @change
- На чекбоксе v-model даёт булево; на группе чекбоксов с одним массивом - список отмеченных значений
- На радио и select v-model хранит выбранное значение; для select это значение option
- Модификаторы: .lazy синхронизирует по change вместо input, .number приводит к числу, .trim обрезает пробелы
- На пользовательском компоненте v-model строится через defineModel и сворачивает props + emit в один реактивный ref
Связанные уроки
- vue-14-event-handling — v-model раскладывается в привязку value и слушатель события input, поэтому опирается на обработку событий
- vue-10-component-communication — defineModel на компоненте - частный случай связки props + emit из урока про коммуникацию