컴포넌트를 만든 후 해당 컴포넌트에
v-model
을 적용하려면@update:modelValue
이벤트를 사용하여v-model
을 만들 수 있다.
컴포넌트에 v-model
을 적용한다는게 무슨말이냐면,
우리가 기본적으로 v-model로 반응형 변수를 양방향 바인딩 시켰던것처럼,
import해서 쓰는 컴포넌트에서도 양방향 바인딩을 적용하고 싶다는 것이다.
우리가 이름을 쓰는 창을 A컴포넌트로 만들고 부모창에서 A를 불러와서 썼다고 가정해보자.
A컴포넌트에서 입력되는 '123123124'라는 값이 v-model
처럼 부모창의 반응형 변수 username
과 양방향 바인딩 되길 바란다.
이 때 필요한것이 컴포넌트에 v-model
을 적용하는 것이다.
<LabelInput label="이름" v-model="username" />
지금부터 그 방법을 알아보자. 먼저 내부동작부터 살펴보겠다.
기본적으로 v-model
은 html요소인 <input>
태그에서 사용한다.
<input v-model="username" />
위의 v-model
은 다음과 같은 내부동작을 가지고 있다.
<input
:value="username"
@input="username = $event.target.value"
/>
위의 기본동작 대신 우리가 만든 컴포넌트는 다음과 같이 수행할 것이다.
<LabelInput
:modelValue="username"
@update:modelValue="newValue => username = newValue"
/>
부모에서Props
로 modelValue
를 넘겨주고,event
로 update
를 받는다.
이해가 쉽지않다.
예시로 하나하나 설명해보겠다.
예를들어 자식컴포넌트 LabelInput이 있다고 가정해보자.
<!--LabelInput 컴포넌트 -->
<template>
<label>
{{ label }}
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</label>
</template>
<script>
export default {
props: ['modelValue', 'label'],
emits: ['update:modelValue'],
};
</script>
props
의 modelValue
를 :value
속성에 바인딩 해줬다.@input
이벤트에서 새 update:modelValue
이벤트로 내보낸다.컴포넌트는 다 만들었다.
이제 부모창에서 LabelInput
컴포넌트를 불러와서 써보자.
<!-- TheView 부모 컴포넌트 -->
<LabelInput
v-model:modelValue="username"
@update:modelValue="newValue => username = newValue"
/>
이제 컴포넌트에 v-model
이 연동되었다.
전체적인 기작을 정리해보자.
① 부모 컴포넌트에서 자식 컴포넌트에게props
로 modelValue="username"을
보낸다.
② 자식 컴포넌트에서 props
로 받은 modelValue
를 <input>
태그에
:value="modelValue"로 연결한다.
③ 자식 컴포넌트에서 입력된 값이 @input
이벤트를 통해 부모로 보내진다.
update:modelValue
로 $event.target.value
를 내보낸다.
④ 부모 컴포넌트는 받은 파라미터 값으로 username
을 업데이트 시킨다.
@update:modelValue="newValue => username = newValue"
⑤ 위와같이 이름을 입력하는 창이 자식 컴포넌트 창이라면,
자식컴포넌트 인풋창에 입력한 값이 부모컴포넌트의 username
에 v-model
로 바인딩 된 것처럼 동작을 할 것이다.
이것이 바로 컴포넌트에 v-model
을 적용한 것이다.
<LabelInput
:modelValue="username"
@update:modelValue="newValue => username = newValue"
/>
위의 코드를 다음과 같이 v-model
로 바꿀 수 있다.
<LabelInput v-model="username" label="이름" />
컴포넌트 안에서 computed를 사용하여 v-model
을 구현할 수 있다.
이 경우, 템플릿의 코드가 훨씬 간단해진다.
<!--LabelInput 컴포넌트 -->
<template>
<label>
{{ label }}
<input v-model="value" type="text" />
</label>
</template>
<script>
export default {
props: ['modelValue', 'label'],
emits: ['update:modelValue'],
setup(props, { emit }){
const vaue = computed({
get(){
return props.modelValue;
},
set(value){
emit('update:modelValue', value);
},
});
return { value };
}
};
</script>
기본적으로 v-model
은 컴포넌트에서 modelValue
props와 update:modelValue
이벤트로 사용한다.
하지만 전달인자를 사용하여 이름을 수정할 수 있다.
<LabelInput v-model:title="username" label="이름" />
이 경우 자식 컴포넌트에서는 :title
을 속성으로 정의하고 update:title
로 이벤트를 내보내야 한다.
<!--LabelInput 컴포넌트 -->
<template>
<label>
{{ label }}
<input v-model="value" type="text" />
</label>
</template>
<script>
export default {
props: ['title', 'label'],
emits: ['update:title'],
setup(props, { emit }){
const vaue = computed({
get(){
return props.title;
},
set(value){
emit('update:title', value);
},
});
return { value };
}
};
</script>
v-model
전달인자를 사용하여 컴포넌트에 여러 v-model
을 바인딩할 수 있다.
<LabelInput v-model:title="title"
v-model:author="author" />
이제 LableInput
컴포넌트를 보자.
<!--LabelInput 컴포넌트 -->
<template>
<div>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</div>
<div>
<input
type="text"
:value="author"
@input="$emit('update:author', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['title', 'author'],
emits: ['update:title', 'update:author'],
};
</script>
필요에 따라 v-model 수식어를 추가할 수 있다.
예를 들어 첫 글자를 대문자로 표시하는 capitalize 라는 수식어를 만들어 보자.
<CustomInput v-model.capitalize="username"></CustomInput>
컴포넌트에 추가된 수식어는 modelModifiers prop을 통해 컴포넌트에 전달된다.
이제 이벤트를 내보내기 전에 문자열 첫 글자를 대문자로 만들면된다.
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
<script>
export default {
props: {
modelValue: String,
modelModifiers: { default: () => ({}) },//기본값이 빈 객체인 modelModifiers props
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const emitValue = (e) => {
let value = e.target.value;
if (props.modelModifiers.capitalize) {//수식어가 넘어오면 true
value = value.charAt(0).toUpperCase() + value.slice(1);
}
emit('update:modelValue', value);
};
return {
emitValue,
};
},
};
</script>
vue3 완벽마스터-짐코딩
vue3 공식문서