Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

v-model на компонентах

v-model работает не только с полями ввода, но и с вашими компонентами. Это удобно, когда компонент редактирует одно значение родителя: строку поиска, выбранный фильтр, текст новой задачи. Для этого есть макрос defineModel, доступный с Vue 3.4.

Компонент с defineModel

Создайте src/components/TodoInput.vue:

<!-- src/components/TodoInput.vue -->
<script setup>
const text = defineModel()
</script>
 
<template>
  <input v-model="text" placeholder="Что нужно сделать?" class="todo-input">
</template>
 
<style scoped>
.todo-input {
  padding: 8px;
  width: 100%;
}
</style>

defineModel() возвращает ref, связанный со значением родителя. Чтение даёт значение из родителя, а запись сообщает родителю новое значение.

Использование

<!-- src/App.vue -->
<script setup>
import { ref } from 'vue'
import TodoInput from './components/TodoInput.vue'
 
const newTodo = ref('')
</script>
 
<template>
  <TodoInput v-model="newTodo" />
  <p>Новая задача: {{ newTodo }}</p>
</template>

Текст под полем повторяет ввод — значение из компонента попадает в newTodo родителя. Если родитель запишет в newTodo пустую строку, поле в TodoInput очистится.

Что происходит внутри

v-model на компоненте — сокращение для параметра и события:

<TodoInput
  :modelValue="newTodo"
  @update:modelValue="(value) => (newTodo = value)"
/>

defineModel объявляет и параметр modelValue, и событие update:modelValue, поэтому писать defineProps и defineEmits вручную не нужно. Так же устроены компоненты в старом коде, написанном до Vue 3.4.

Несколько моделей

Модели можно дать имя и использовать несколько v-model на одном компоненте. Создайте src/components/TodoFilter.vue:

<!-- src/components/TodoFilter.vue -->
<script setup>
const filter = defineModel('filter', { default: 'all' })
const query = defineModel('query', { default: '' })
</script>
 
<template>
  <input v-model="query" placeholder="Поиск">
  <button @click="filter = 'all'">Все</button>
  <button @click="filter = 'active'">Активные</button>
  <button @click="filter = 'done'">Выполненные</button>
</template>
<!-- src/App.vue, фрагмент шаблона -->
<TodoFilter v-model:filter="filter" v-model:query="query" />
<p>Фильтр: {{ filter }}, поиск: {{ query }}</p>

Кнопки меняют filter в родителе, а поле — query. Переменные filter и query объявлены в App.vue через ref.

Частая ошибка

Принять значение через defineProps и привязать к нему v-model внутри компонента:

<script setup>
const props = defineProps(['modelValue'])
</script>
 
<template>
  <input v-model="props.modelValue">
</template>

Так компонент пытается изменить параметр, который принадлежит родителю, и Vue выдаст предупреждение, а родитель изменения не получит. Используйте defineModel — он отправит изменение родителю через событие.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.