| Vue: двустороннее связывание |
|
| Добавил(а) microsin |
|
Разберем двустороннее связывание (two-way binding) на примере синхронизации хранимого в переменной значения и его визуального представления на метке и поле ввода. Предположим, что у нас есть метка, на которой отображается имя города и соответствующая переменная city. Также есть поле ввода, где можно менять значение этой переменной. Это называется двусторонним связыванием, и его можно реализовать несколькими способами. Для тестирования two-way binding нам понадобится для примера простой тестовый проект. 1. Создайте проект командами: npm create vue@latest Здесь "two-way-bind-example" это имя проекта, которое было указано при его создании. Оно совпадает с именем корневого каталога проекта. В результате получится компонент приложения App.vue, удалите из него все лишее: // App.vue < script setup>< /script> 2. Добавьте простейший компонент Input.vue и поместите его в папку src/components: // Input.vue < script setup>< /script> 3. Добавьте в App.vue использование компонента Input.vue: // App.vue < script setup> 4. Добавьте в App.vue переменную city: // App.vue < script setup> Итак, у нас есть компонент App.vue и компонент ввода Input.vue. Необходимо реализовать двустороннюю связь (two-way binding) значения этой переменной с тем, что вводится в Input. [ref, defineEmits и defineProps] Чтобы из компонента Input сообщить наверх, что произошли какие-то изменения при вводе, необходимо применить defineEmits. Передаваемое значение обычно обозначают в форме "событие:значение" следующим образом: // Input.vue < script setup> Эта запись говорит, что у нас есть некоторое значение (value), которое мы теперь обновили (update). Для генерации события при вводе необходимо в шаблоне компонента Input сделать такую запись: // Input.vue < template> < input @input="emit('update:value', $event.target.value)" /> < /template> Здесь @input это событие, которое говорит о том, что произошел ввод текста. "emit('update:value', $event.target.value)" обозначает функцию, которая передает введенное значение. Теперь при каждом нажатии кнопки будет передаваться событие о вводимом значении в родительский компонент App.vue. Чтобы прочитать передаваемое значение, в родительском компоненте App.vue это значение надо принять: // App.vue < script setup> Здесь @update:value="updateCity" обозначает прием генерируемого компонентом Input события, а updateCity это функция обработчика события. В ней вставлен вызов console.log, чтобы можно было в консоли браузера видеть вводимые изменения. Чтобы вывести вводимое значение в родительском компоненте App.vue, необходимо с помощью ref обозначить его состояние: // App.vue < script setup> Вывод значения состояния: // App.vue < template> {{ city }} < Input placeholder="Введите город" @update:value="updateCity"/> < /template> Вводимое значение будет сразу появляться на форме родительского компонента. Теперь надо задать какое-то значение по умолчанию, например "Москва", и обеспечить его появление в поле ввода как начальное значение. Это и будет "two-way binding". // App.vue Для этого в компоненте Input необходимо определить defineProps: // Input.vue < script setup> Созданное свойство props необходимо привязать с помощью v-bind к полю ввода в шаблоне: // Input.vue < template> < input :value="props.value" @input="emit('update:value', $event.target.value)" /> < /template> С одной стороны, мы теперь при каждом вводе эмитим событие @input и передаем наверх вводимое значение, а с другой стороны мы получаем props и кладем его в поле ввода как значение по умолчанию. Для этого в родительском компоненте App.vue также надо обновить использование компонента Input: // App.vue < template> {{ city }} < Input placeholder="Введите город" :value="city" @update:value="updateCity" /> < /template> Теперь мы в App.vue слушаем событие ввода (@update:value="updateCity"), и одновременно передаем в поле ввода некоторое хранимое значение (:value="city"). Это и есть так называемое "two-way binding", когда мы связали двусторонней связью не только чтение, но и запись какого-то сохраненного значения (let city=ref("Москва")). Мы можем как задавать значение, так и обновлять его. Очевидно, что такой способ двустороннего связывания получился довольно громоздким, поэтому существует удобная альтернатива: v-model. [v-model] Это более простой для использования способ двустороннего связывания. В шаблоне App.vue исправьте атрибуты компонента Input, заменив :value="city" и @update:value="updateCity" на один атрибут v-model="". Функцию updateCity теперь также можно удалить: // App.vue < script setup> Также надо модифицировать компонент Input: удалить defineProps и defineEmits, и добавить определение модели с помощью defineModel. // Input.vue < script setup> После этого в шаблоне можно использовать model в качестве привязанного через v-bind значения, и событие @input также определить не через emit, а через model: // Input.vue < template> < input :value="model" @input="model=$event.target.value" /> < /template> Это все что нужно для организации двусторонней связи. Также v-model может автоматически связываться с поведением стандартных элементов интерфейса ввода: input, textarea, checkbox, radio, selected, multiselect, все это будет работать. Т. е. можно заменить :v6alue= и @input= на единственный атрибут v-model="model", код становится еще более лаконичным // Input.vue < template> < input v-model="model" /> < /template> 1. Для defineModel() можно и нужно в качестве параметра задать тип значения, например: < script setup> 2. Второй параметр, который мы можем передать в defineModel, это required: const data = defineModel({ type: String, required: true }); Этот параметр обязывает использовать v-model, и полезен для случаев, когда разрабочик забыл его указать. В этом случае в консоль браузера будет выводиться предупреждение: [Vue warn]: Missing required prop: "modelValue". 3. Третьим параметром в defineModel можно передать значение по умолчанию, если мы его не передали, например: const data = defineModel({ type: String, required: true, default: "Превед" }); [Именованные v-model] 4. Существует возможность использования именованных моделей, для этого в defineModel можно передать её произвольное пользовательское имя, например: const data = defineModel({ type: String, required: true, default: "Превед" }); Здесь "mymodel" задает имя модели. Теперь можно указывать в атрибутах несколько моделей с помощью синтаксиса v-model:имя_модели, например (здесь в vmodel:mymodel="city" вместо city может быть другая переменная): < Input v-model="city" vmodel:mymodel="city" placeholder="Введите город" /> Тем самым мы можем задавать несколько моделей в нашем компоненте, просто задавая моделям понятные имена. // Input.vue < script setup> В Vue двустороннее связывание с меткой (label) можно организовать несколькими способами. Вот основные подходы: 1. Использование v-model для двустороннего связывания < template> < div> < !-- Метка, отображающая значение --> < p>Значение: {{ inputValue }}< /p> 2. Связывание с конкретным элементом через `for` и `id` < template> < div> < !-- Метка связана с полем ввода через id --> < label for="userInput">Введите имя:< /label> 3. Кастомный компонент с более сложной логикой < template> < div> < custom-label :value="textValue" label="Текст:" @update="textValue = $event" /> < /div> < /template> 4. Использование вычисляемых свойств для форматирования < template> < div> < !-- Форматированное отображение --> < p>Отформатированное значение: {{ formattedValue }}< /p> 5. Composition API (Vue 3) < template> < div> < label>Счётчик: {{ count }}< /label> < input v-model.number="count" type="range" min="0" max="100" > < p>Текущее значение: {{ count }}< /p> < /div> < /template> Ключевые моменты: 1. v-model - основная директива для двустороннего связывания. Выбор подхода зависит от конкретной задачи: - Простое отображение: `v-model` + интерполяция `{{ }}`. [Ссылки] 1. Vue 3, быстрый старт. |