본문 바로가기
관리자

Programming-[Frontend]/Vue.js

Vue.js / 기초 / 8. Emit으로 자식 -> 부모 컴포넌트에 데이터 보내기

728x90
반응형

 

1. 부모로부터 props를 받는 자식 컴포넌트 작성

지난 글에서 다룬 것처럼, 일단 부모로부터 props를 받는 자식 컴포넌트를 작성한다. 자식 컴포넌트로 InputField.vue를 만들고 이것을 부모 컴포넌트 Home.vue에서 불러오는 방식이다.

 

[부모컴포넌트 Home.vue]

inputField를 불러오고, 자신의 data인 title을 title 속성으로 바인딩하여 props로 내려준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
  <div>
    <h1>Welcome to Home</h1>
    <inputField :title = "title"/>    
  </div>
</template>
<script>
import inputField from '@/components/layout/inputField';
 
export default {
  data() {
    return {
      title : ""
    }
  },
  components : {
    inputField
  }
}
</script>
cs

 

 

[자식 컴포넌트 inputField.Vue]

props로 title을 받아와서 input의 value 속성으로 바인딩 해주었다. 이렇게 하면 부모 컴포넌트로부터 props로 받아온 title 값은 input의 value 값으로 지정되지만, 사용자가 input box에 입력한 값은 props.title 값으로 지정되지 않는다. 이를 해결하는 방법으로 5번 줄의 주석문과 같이 v-model을 사용할 수 있겠지만, props로 내려받은 값은 자식 컴포넌트에서 직접 변경을 하면 안된다는 원칙에 따라 v-model을 적용할 수 없다. 해결 방법은 @input 속성을 사용하는 것이다(다음 섹션에서 자세히 설명).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
  <div>
    <form>
      <label>ID</label>
      <!-- <input type="text" v-model = "title" style = "padding: 20px; border: 1px pink solid"> -->
      <input type="text" :value = "title" style = "padding: 20px; border: 1px pink solid">
      <button>Submit</button>
    </form>
  </div>
</template>
 
<script>
export default {
  props : {
    title : {
      type : String,
      required : true
    }
  }
}
</script>
cs

 

@input 사용하기

이전 글(Vue.js / 기초 / 데이터 양방향 바인딩(v-model), computed, watch 속성) 에서 언급한 바와 같이, @input을 이용하면 input 태그에 입력되는 값을 event로 출력할 수 있게 된다. @input은 input 박스에 입력이 발생할 때마다 지정된 메서드를 실행시키는 속성이다.

 

1
2
3
4
5
6
7
8
9
10
11
<input type="text" :value = "title" @input = "titleUpdate" style = "padding: 20px; border: 1px pink solid">
 
...
 
<script>
  methods : {
    titleUpdate(e) {
      console.log(e.target.value)
    }
  }
</script>
cs

콘솔로그로 input 입력값 살펴보기

 

 


 

2. 부모 컴포넌트로 변경값 보내기 : $Emit

 

2-1. 메서드 활용하기

input 박스에 입력한 값을 페이지에 그대로 표시해주기 위해서, 이제 해야할 것은 input으로 입력 받은 값을 title 값에 넣어주는 것이다. 그러나 앞서 이야기한 것처럼, 자식 컴포넌트에서 props.title값을 직접 변경해서는 안된다. 따라서 자식 컴포넌트의 input에서 변경된 값을 부모 컴포넌트의 title 값으로 업데이트 하도록 해야한다. 여기서 $Emit이라는 새로운 문법을 배운다.

 

우선 자식 컴포넌트에서 @input 이벤트에 this.$emit을 작성한다. 첫번째 인자는 부모 컴포넌트에서 자식 컴포넌트를 호출할때 적을 메서드 이름, 두번째 인자는 해당 메서드에 들어갈 값으로 지정한다.

 

[자식 컴포넌트]

1
2
3
4
5
6
7
8
9
10
11
<input type="text" :value = "title" @input = "titleUpdate" style = "padding: 20px; border: 1px pink solid">
 
...
 
<script>
  methods : {
    titleUpdate(e) {
      this.$emit('titleFromChild', e.target.value)
    }
  }
</script>
cs

 

그리고나서 부모 컴포넌트에서 자식 컴포넌트를 호출하는 부분에 @메서드 이름으로 부모의 메서드와 바인딩을 시켜준다. 부모 컴포넌트에서 새로 생성한 titleChange 메서드는 부모의 this.title 값을 titleChange 메서드가 titleFromChild 메서드로부터 받아오는 인자값 title로 지정해주게 된다.

 

[부모 컴포넌트]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
  <div>
    <h1>Welcome to Home</h1>
    <inputField :title = "title" @titleFromChild="titleChange"/>    
    {{ title }}
  </div>  
</template>
 
...
 
<script>
  date() {
    return {
      title : ""
    }
  },
 
...,
 
  methods: {
    titleChange(title) {
      this.title = title
    }
  }
</script>
cs

 

 

결과화면은 다음과 같다.

2-2. 메서드 생략하고 $event 활용하기

부모와 자식 컴포넌트에 각각 메서드를 작성하고 사용할 필요없이, 자식 컴포넌트에서 정의한 $emit을 바로 부모로 넘겨주는 방법도 있다.

 

[자식 컴포넌트]

 

기존 titleUpdate 메서드는 주석처리 해버리고, @input 속성에 $emit을 직접 작성하였다. 여기서 두번째 인자로 받는 전달해줄 값은 $event로 지정해주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
  <div>
    <form>
      <label>ID</label>
      <input type="text" :value = "title" @input = "$emit('titleFromChild', $event)" style = "padding: 20px; border: 1px pink solid">
      <button>Submit</button>      
    </form>
  </div>
</template>
 
<script>
export default {
  props : {
    title : {
      type : String,
      required : true
    }
  },
  // methods : {
  //   titleUpdate(e) {
  //     this.$emit('titleFromChild', e.target.value)
  //   }
  // }
}
</script>
cs

 

[부모 컴포넌트]

부모 컴포넌트에서도 역시나 method를 주석처리하고, 자식 컴포넌트인 inputField 호출 부분에 자식 컴포넌트에서 정의한 titleFromChild 메서드를 속성으로 달아주었다. 그리고 여기서 바로 title 값을 $event.target.value 값으로 지정해준다. 처리 완료 후 실행 시 정상작동 되는 것을 확인할 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
  <div>
    <h1>Welcome to Home</h1>
    <inputField :title = "title" @titleFromChild="title = $event.target.value"/>    
    {{ title }}
  </div>  
</template>
<script>
import inputField from '@/components/layout/inputField';
 
export default {
  data() {
    return {
      title : ""
    }
  },
  components : {
    inputField
  },
  // methods: {
  //   titleChange(title) {
  //     this.title = title
  //   }
  // }
}
</script>
cs

 

2-3. v-model로 부모-자식 컴포넌트 간 양방향 바인딩 하기

지금까지 작성한 것은 부모에서 자식으로 props를 내려주고, 자식에서 $emit으로 event값을 부모로 올려준 다음, 부모에서 props로 내려주는 data 값을 event 값으로 변경해주는 방식이였다. 이것을 조금 더 단순화하는 방식이 v-model이다.

 

맨 처음 언급한 것처럼 v-model을 html 태그에 적용하면 양방향 바인딩이 되어 props를 자식 컴포넌트에서 수정하게 되지만, 컴포넌트 자체에 v-model을 걸게되면 부모-자식 컴포넌트 간 바인딩이 이루어져서 자식에서 직접 props를 바꾸는 것이 아니라 부모 컴포넌트에서 props에 해당하는 data 값을 바꾸는 방식이 된다.

 

[부모 컴포넌트]

자식 컴포넌트를 호출하는 부분에 v-model="title"로 양방향 바인딩을 해준다. 이것은 자식 컴포넌트로부터 $emit으로 올라오는 값을 title 값으로 업데이트하겠다는 의미가 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
  <div>
    <h1>Welcome to Home</h1>
    <inputField v-model="title" /> 
    {{ title }}
  </div>  
</template>
<script>
import inputField from '@/components/layout/inputField';
 
export default {
  data() {
    return {
      title : ""
    }
  },
  components : {
    inputField
  },
  // methods: {
  //   titleChange(title) {
  //     this.title = title
  //   }
  // }
}
</script>
cs

 

[자식 컴포넌트]

부모 컴포넌트에서 v-model로 양방향 바인딩을 하면, 자식으로 전달하는 title 값은 props.title이 아니라 props.value(15번줄) 값으로 지정이 된다. v-model로 양방향 바인딩을 하면 value로 묶인다. 그리고 6번줄 $emit 부분에서 메서드 이름은 input, 올려줄 값은 $event.target.value 값으로 변경해주어야 한다. 이렇게하면 정상적으로 작동하는 것을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
  <div>
    <form>
      <label>ID</label>
      <!-- <input type="text" v-model = "title" style = "padding: 20px; border: 1px pink solid"> -->
      <input type="text" :value = "value" @input = "$emit('input', $event.target.value)" style = "padding: 20px; border: 1px pink solid">
      <button>Submit</button>      
    </form>
  </div>
</template>
 
<script>
export default {
  props : {
    value : {
      type : String,
      required : true
    }
  },
  // methods : {
  //   titleUpdate(e) {
  //     this.$emit('titleFromChild', e.target.value)
  //   }
  // }
}
</script>
cs

 

 


 

 

참조

1) 유튜브 코지코더 - '뷰js 2 기초익히기 시리즈'

www.youtube.com/channel/UCI4tTBupvhMX1aWDSm-HAXw

728x90
반응형