v-model과 emit에 대해서

emit

v-model을 사용가능한 컴포넌트

  • <input>

  • <select>

  • <textarea>

v-model의 수식어

  • .lazy - input 대신 change 이벤트를 수신함.

  • .number - 유효한 입력 문자열을 숫자로 변환하여 전달.

  • .trim - 사용자 입력의 공백을 트리밍. (e.g : v-model.trim="inputValue")




1.단방향 바인딩

아래의 input으로 내용을 변경해도 msg는 변경되지않는다

그러나 msg 변수를 직접 변경함으로써 변경된다

const msg=ref('test')

<input type="text" :value="msg" />
<h1>{{ msg }}</h1>


2. 양방향 바인딩 (v-model 사용)

아래처럼 v-model을 사용하면 input에서 값을 변경하면 msg가 변경된다

msg변수가 변경된다

const msg=ref('test')
<input v-model="msg" />
<h1>{{ msg }}</h1>

 ↓

실제로는 내부적으로는 아래와 같이 컴파일되어 처리된다

<input
  :value="msg"
  @input="msg = $event.target.value"
/>


3.일반적인 Emit 사용 예제

** 부모 컴포넌트 **
const msg=ref('test')
function changeMsgFunc(newValue){
    msg.value = newValue
}

<자식컴포넌트 :msg="msg" @change-msg="changeMsgFunc" />

** 자식 컴포넌트 **
const emit = defineEmits(["change-msg"]);
...
emit('change-msg','변경값')


4.Emit 양방향 바인딩 (v-model사용)

참고링크

4-1 간단한 방법

v-model만 사용하는 방법

<!-- Parent.vue -->
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
  <Child v-model="msg" />
</template>

<!-- Child.vue -->
<script setup>
const model = defineModel()
</script>

<template>
  <span>My input</span> 
  <input v-model="model">
</template>


4-2 조금 긴 방법

update:modelValue을 사용하는 방법

<!-- 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>

<!-- Parent.vue -->
<Child
  :modelValue="foo"
  @update:modelValue="$event => (foo = $event)"
/>


조금 긴 방법을 사용했을때 내부구조 ↑

아래처럼 v-model을 사용하면 부모컴포넌트에서 emit을 선언하지않아도 msg변수가 변경된다

** 부모 컴포넌트 **
const msg=ref('test')

<자식컴포넌트 v-model="msg"/>

 ↓

실제로는 내부적으로는 아래와 같이 컴파일되어 처리된다

** 부모 컴포넌트 **
const msg=ref('test')

<자식컴포넌트
  :modelValue="mgs"
  @update:modelValue="newValue => msg = newValue"
/>


** 자식 컴포넌트 **

defineProps(['modelValue'])
defineEmits(['update:modelValue'])
// const emit = defineEmits(['update:modelValue'])
// emit('update:modelValue', 입력값..)

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>



5.Emit 다른 사용법

굳이 v-model이 아니라도 위의 예제를 변형해 아래와 같이 사용가능

** 부모 컴포넌트 **
const msg=ref('test')
<자식컴포넌트
  :checked="msg"
  @update:checked="newValue => msg = newValue"
/>

** 자식 컴포넌트 **
defineProps(['checked'])
defineEmits(['update: checked'])
<template>
  <input
    :value="checked"
    @input="$emit('update:checked', $event.target.value)"
  />
</template>



2024/01/16 기본적인 쌍방향 통신 예제


부모 컴포넌트 (ParentComponent.vue)

<template>
  <section class="container">
    <Child :message='testData' @update='update'></Child>
  </section>
</template>
 
<script setup>
import { ref } from 'vue';
import Child from './Comp.vue';
 
const testData = ref('beforeData');

function update(message) {
  testData.value = message
}
</script>


자식 컴포넌트 (ChildComponent.vue)

<template>
  <h2>{{ message }}</h2>
  <button @click="change()">change</button>
</template>
 
<script setup>
import { ref } from 'vue';
import { defineProps, defineEmits } from "vue"
 
const props = defineProps(["message"]);
const emit = defineEmits(["update"])
 
const messageLocal=ref(props.message)

function change(){
  messageLocal.value="afterData"
  emit("update", messageLocal.value)
}
</script>


2024/01/20 추가 ( 위의 예제를 말로 풀면 아래와 느낌 )

1.부모 ref정의후 props로 넘긴다

2.자식 props로 받은후 해당 props를 HTML에 기입한다 ( * 만약 여기서 props가 아니라 props를 ref에 넣은 변수등을 HTML에 넣으면 변화가 적용이 안된다 )

3.emit으로는 부모에게 보낼땐 props가 변경이 안되기때문에 새로운 변수등에 할당해서 보낸다


6.자식 컴포넌트에서 데이터 변경하는 경우 (24/2/4추가)

부모에서 자식으로 props로 전달한 데이터는

자식에서 받은 props를 그대로 설정하기만해도

부모에서 데이터 변경시 자식 데이터가 자동으로 변경이 된다

그러나 간혹 자식에서 부모의 데이터를 변경해야하는 경우가 있는데

이 경우는 props를 직접 변경할수 없기때문에 그에 따른 대처법이다


1.computed를 사용

아래의 1 ,2, 3 순서로 데이터를 전달하며 부모에 emit된 값을 다시 props로 받아온뒤 1이 실행된다

<input v-model="modifiedData"/>  // 2 
// 여기서 v-model을 사용하면 내부적으로 자체적인 @input함수( event => text = event.target.value )를 사용하게 되며 3번이 호출된다

const modifiedData = computed({
  // 1 
  get: () => parentData.value,
  // 3
  set: (value) => {
    emit('updateData', value);
  },
});


2.watch사용

ref(props.parentData)를 사용하면 초기 값만 설정되고, 부모 컴포넌트의 parentData가 변경되어도 자식 컴포넌트의 modifiedData는 자동으로 업데이트되지 않는다.

그러므로 watch를 사용해서 부모로 부터 받는 props.parentData를 감시해야한다.

아래의 1 ,2, 3 순서로 데이터를 전달하며 부모에 emit된 값을 다시 props로 받아온뒤 1이 실행된다

<input :value="modifiedData" @input="notifyParent" />

const modifiedData = ref(props.parentData); // 1 (자식화면에서 사용할 반응성 변수 초기화)

watch(() => props.parentData, (newValue) => {
  modifiedData.value = newValue;  // 4 아래의 3에서 갱신한 부분이 1번에 갱신되지 않기때문에 watch로 부모로 부터 받은 값을 감시해서 갱신해준다
});

const notifyParent = (e) => {
  modifiedData.value = e.target.value; // 2 (자식화면에서 사용할 반응성 변수 갱신)
  emit('updateData', modifiedData.value); // 3 부모값 변경, 그러나 1번 부분이 갱신되지않음
};



참조

v-model참조

v-model참조2

v-model참조3

emit사용법

Jan 10, 2024 Views 269