v-model در کامپوننت
استفاده پایه
با استفاده از v-model روی یک کامپوننت میتوان یک اتصال دوطرفه ایجاد کرد.
از Vue 3.4 به بعد، رویکرد توصیه شده برای انجام این کار استفاده از ماکرو defineModel() است:
vue
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>سپس والد میتواند مقدار را با v-model متصل کند:
template
<Child v-model="countModel" />مقدار برگردانده شده توسط defineModel() یک ref است. میتوان به آن دسترسی یافت و آن را تغییر داد مانند هر ref دیگری، به جز اینکه به عنوان یک اتصال دوطرفه بین داده والد و داده محلی عمل میکند:
.valueآن با مقدار متصل شده توسطv-modelوالد همگامسازی میشود؛- هنگامی که توسط فرزند تغییر داده شود، باعث بهروزرسانی مقدار متصل شده والد نیز میشود.
این بدان معناست که میتوانید این ref را نیز با v-model به یک عنصر input ساده متصل کنید، که پیادهسازی عناصر input را در حال استفاده از v-model را ساده میسازد:
vue
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>در زیر پوسته
defineModel یک ماکرو میانبر است. کامپایلر آن را برای موارد زیر توسعه میدهد:
- یک پراپ به نام
modelValueکه مقدار ref محلی با آن همگامسازی میشود. - یک رویداد به نام
update:modelValueکه هنگام تغییر مقدار ref محلی ارسال میشود.
روش پیادهسازی همان کامپوننت فرزند نشان داده شده در بالا قبل از نسخه 3.4 به این صورت است:
vue
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>سپس v-model="foo" در کامپوننت والد کامپایل میشود به:
template
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>همانطور که میبینید، کمی طولانیتر است. با این حال، درک آنچه در زیر پوسته اتفاق میافتد مفید است.
از آنجا که defineModel یک پراپ تعریف میکند، بنابراین میتوانید آپشنهای زیرین پراپ را با پاس دادن آن به defineModel اعلام کنید:
js
// v-model اجباری کردن
const model = defineModel({ required: true })
// ارائه یک مقدار پیشفرض
const model = defineModel({ default: 0 })هشدار
اگر برای پراپ defineModel مقدار default تعریف کرده باشید و هیچ مقداری به آن از کامپوننت والد پاس ندهید، میتواند باعث ناهمگامسازی (de-synchronization) بین کامپوننتهای والد و فرزند شود. در مثال زیر myRef والد برابر undefined است، اما model در فرزند برابر ۱ است:
vue
<script setup>
const model = defineModel({ default: 1 })
</script>vue
<script setup>
const myRef = ref()
</script>
<template>
<Child v-model="myRef"></Child>
</template>آرگومانهای v-model
همچنین v-model روی کامپوننت میتواند آرگومان بپذیرد:
template
<MyComponent v-model:title="bookTitle" />در کامپوننت فرزند، میتوانیم آرگومان مربوطه را با پاس دادن یک رشته به عنوان اولین آرگومان defineModel() پشتیبانی کنیم:
vue
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>اگر آپشنهای پراپ نیز مورد نیاز است، باید پس از نام مدل پاس داده شوند:
js
const title = defineModel('title', { required: true })استفاده قبل از 3.4
vue
<script setup>
defineProps({
title: {
required: true
}
})
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>اتصال v-model چندگانه
با استفاده از توانایی هدف قراردادن یک پراپ و رویداد خاص که در آرگومانهای v-model یاد گرفتیم، حالا میتوانیم اتصالهای v-model چندگانه، روی یک کامپوننت تکی ایجاد کنیم.
هر v-model بدون نیاز به گزینههای اضافه در کامپوننت با یک پراپ مختلف همگامسازی میشود:
template
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>vue
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>استفاده قبل از 3.4
vue
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>مدیریت modifierهای v-model
وقتی درحال یادگیری اتصالهای input در formها بودیم، مشاهده کردیم که v-model دارای modifierهای داخلی .trim و .number و .lazy بود. گاهی اوقات همچنین ممکن است بخواهید که v-modelای که روی کامپوننت سفارشیشده input خود قرار دادید هم قابلیت پشتیبانی از modifierهای سفارشی را داشته باشد.
بیایید یک نمونه modifier سفارشی بسازیم، capitalize که وظیفه داشته باشد حرف اول هر رشتهای که توسط اتصال v-model فراهمشده را به حالت بزرگ آن تبدیل کند:
template
<MyComponent v-model.capitalize="myText" />میتوان به modifierهای اضافه شده به یک v-model در کامپوننت فرزند با ساختارشکنی مقدار بازگشت داده شده از defineModel() به این صورت دسترسی یافت:
vue
<script setup>
const [model, modifiers] = defineModel()
console.log(modifiers) // { capitalize: true }
</script>
<template>
<input type="text" v-model="model" />
</template>برای تنظیم شرطی نحوه خواندن/نوشتن مقدار بر اساس modifierها، میتوان آپشنهای get و set را به defineModel() پاس داد. این دو آپشن مقدار داده را در هنگام خواندن/نوشتن ref مدل دریافت میکنند و باید مقدار تبدیل شده را برگردانند. در ادامه نحوه استفاده از آپشن set برای پیادهسازی modifier مورد نظر یعنی capitalize را میبینیم:
vue
<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>
<template>
<input type="text" v-model="model" />
</template>استفاده قبل از 3.4
vue
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="props.modelValue" @input="emitValue" />
</template>modifierها برای v-model های آرگوماندار
اینجا مثال دیگری از استفاده از پیرایندهها با v-model چندگانه با آرگومانهای مختلف را مشاهده میکنید:
template
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>vue
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>استفاده قبل از 3.4
vue
<script setup>
const props = defineProps({
firstName: String,
lastName: String,
firstNameModifiers: { default: () => ({}) },
lastNameModifiers: { default: () => ({}) }
})
defineEmits(['update:firstName', 'update:lastName'])
console.log(props.firstNameModifiers) // { capitalize: true }
console.log(props.lastNameModifiers) // { uppercase: true }
</script>