[Vue.js] 컴포넌트에 v-model 적용하기

토끼는 개발개발·2023년 9월 20일
0

Vue.js

목록 보기
14/19
post-thumbnail

📌 컴포넌트에 v-model 만들기

1. 기본 동작 및 예시

컴포넌트를 만든 후 해당 컴포넌트에 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"
/>

부모에서PropsmodelValue를 넘겨주고,eventupdate를 받는다.



이해가 쉽지않다.
예시로 하나하나 설명해보겠다.

예를들어 자식컴포넌트 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>
  • propsmodelValue: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"

⑤ 위와같이 이름을 입력하는 창이 자식 컴포넌트 창이라면,
자식컴포넌트 인풋창에 입력한 값이 부모컴포넌트의 usernamev-model로 바인딩 된 것처럼 동작을 할 것이다.

이것이 바로 컴포넌트에 v-model을 적용한 것이다.




2. 컴포넌트에 v-model 적용하기

<LabelInput
	:modelValue="username"
	@update:modelValue="newValue => username = newValue"
/>

위의 코드를 다음과 같이 v-model로 바꿀 수 있다.

<LabelInput v-model="username" label="이름" />

Computed 이용하기

컴포넌트 안에서 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>



3. v-model 전달인자

1) 이름 수정

기본적으로 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>

2) 다중 v-model 바인딩

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>

3) v-model 수식어(Modifiers)핸들링

필요에 따라 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 공식문서

profile
하이 이것은 나의 깨지고 부서지는 샏 스토리입니다

0개의 댓글