Component v-model
v-model
을 컴포넌트에서 사용하여 양방향 바인딩을 구현할 수 있습니다.
먼저 네이티브 엘리먼트에서 v-model이 어떻게 사용되는지 다시 살펴봅시다:
template
<input v-model="searchText" />
내부적으로 템플릿 컴파일러는 v-model
을 좀 더 자세한 표현으로 확장합니다. 따라서 위의 코드는 다음과 같은 작업을 수행합니다:
template
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
컴포넌트에 사용하면 v-model
이 대신 이렇게 확장됩니다:
template
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
하지만 이 기능이 실제로 작동하려면 <CustomInput>
컴포넌트가 두 가지 작업을 수행해야 합니다:
- 네이티브
<input>
앨리먼트의value
속성을modelValue
프로퍼티에 바인딩합니다. - 네이티브
input
이벤트가 트리거되면 새 값으로update:modelValue
사용자 지정 이벤트를 내보냅니다.
실제로 작동하는 모습은 다음과 같습니다:
vue
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
이제 v-model
이 이 컴포넌트와 완벽하게 작동합니다:
template
<CustomInput v-model="searchText" />
이 컴포넌트 내에서 v-model
을 구현하는 또 다른 방법은 getter와 setter가 모두 있는 쓰기 가능한 computed
프로퍼티를 사용하는 것입니다. get
메서드는 modelValue
프로퍼티를 반환하고 set
메서드는 해당 이벤트를 발생시켜야 합니다:
vue
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
<template>
<input v-model="value" />
</template>
v-model
arguments
기본적으로 컴포넌트의 v-model
은 modelValue
를 프로퍼티로, update:modelValue
를 이벤트로 사용합니다. v-model
에 인자를 전달하여 이러한 이름을 수정할 수 있습니다:
template
<MyComponent v-model:title="bookTitle" />
이 경우 자식 컴포넌트는 title
프로퍼티를 예상하고 부모 값을 업데이트하는 update:title
이벤트를 발생시켜야 합니다:
vue
<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
Multiple v-model
bindings
앞서 배운 것처럼 특정 prop과 이벤트를 타깃팅하는 기능을 v-model
인자로 활용하면 이제 단일 컴포넌트 인스턴스에 여러 개의 v-model
바인딩을 생성할 수 있습니다.
각 v-model
은 컴포넌트에서 추가 옵션 없이도 다른 prop에 동기화됩니다:
template
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
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>
v-model
수정자 처리하기
Form 양식 입력 바인딩에 대해 배울 때 v-model
에 .trim
, .number
및 .lazy
와 같은 내장 수정자가 있다는 것을 알았습니다. 경우에 따라 사용자 정의 입력 컴포넌트에서 v-model
이 사용자 정의 수정자를 지원하도록 할 수도 있습니다.
v-model
바인딩에서 제공하는 문자열의 첫 글자를 대문자로 표시하는 사용자 지정 수정자 예제인 capitalize
를 만들어 보겠습니다:
template
<MyComponent v-model.capitalize="myText" />
v-model
컴포넌트에 추가되는 수정자는 modelModifiers
프로퍼티를 통해 컴포넌트에 제공됩니다. 아래 예시에서는 기본적으로 빈 객체로 설정된 modelModifiers
프로퍼티를 포함하는 컴포넌트를 생성했습니다:
vue
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
defineEmits(['update:modelValue'])
console.log(props.modelModifiers) // { capitalize: true }
</script>
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
컴포넌트의 modelModifiers
프로퍼티에 capitalize
가 포함되어 있고 그 값은 v-model
바인딩 v-model.capitalize="myText"
에 설정되어 있기 때문에 true
인 것을 알 수 있습니다.
이제 prop이 설정되었으므로 modelModifiers
객체 키를 확인하고 발신된 값을 변경하는 핸들러를 작성할 수 있습니다. 아래 코드에서는 <input />
앨리먼트가 input
이벤트를 실행할 때마다 문자열을 대문자로 표시합니다.
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="modelValue" @input="emitValue" />
</template>
인자와 수정자가 있는 v-model
에 대한 수정자들
인자와 수정자가 모두 있는 v-model
바인딩의 경우, 생성된 prop 이름은 arg + "수정자"
가 됩니다. 예를 들어
template
<MyComponent v-model:title.capitalize="myText">
해당 선언은 다음과 같아야 합니다:
js
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
다음은 다른 인자를 가진 여러 v-model
에 수정자를 사용한 예시입니다:
template
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
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>