Component v-model
استفاده پایه
با استفاده از v-model
روی یک کامپوننت میتوان یک اتصال دوطرفه ایجاد کرد.
از Vue 3.4 به بعد، رویکرد توصیه شده برای انجام این کار استفاده از ماکرو defineModel()
است:
vue
<!-- Child.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
<!-- Parent.vue -->
<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
<!-- Child.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
<!-- Parent.vue -->
<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
در فرزند برابر ۱ است:
js
// child component:
const model = defineModel({ default: 1 })
// parent component:
const myRef = ref()
html
<Child v-model="myRef"></Child>
آرگومانهای v-model
همچنین v-model
روی کامپوننت میتواند آرگومان بپذیرد:
template
<MyComponent v-model:title="bookTitle" />
در کامپوننت فرزند، میتوانیم آرگومان مربوطه را با پاس دادن یک رشته به عنوان اولین آرگومان defineModel()
پشتیبانی کنیم:
vue
<!-- MyComponent.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
<!-- MyComponent.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>