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Слушаемое событие
Одиночный чекбоксbooleanchange
Группа чекбоксов на массивarray значенийchange
Группа radioзначение выбранногоchange
selectзначение optionchange

Для чекбокса можно задать собственные значения через 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 из урока про коммуникацию
Формы и v-model

0

1

Войти