App.vue 파일(부모)에서 FriendContact.vue 컴포넌트(자식)로 데이터를 전달하기 위해 props를 사용했다.
그럼, 반대로 컴포넌트(자식)에서 부모로의 통신은 어떻게 이뤄질까?
Toggle Favorite 버튼이 좋은 예시가 된다.
현재, isFavorite prop은 생성된 모든 friends에 대해 'true'값으로 설정되어있다.
//App.vue
<friend-contact
v-for="friend in friends"
:key="friend.id"
:name="friend.name"
:phone-number="friend.phone"
:email-address="friend.email"
:is-favorite="friend.isFavorite"
></friend-contact>
data() {
return {
friends: [
{
id: "manuel",
name: "Manuel Lorenz",
phone: "0123 45678 90",
email: "manuel@localhost.com",
isFavorite: true,
},
{
id: "julie",
name: "Julie Johnes",
phone: "0456 35353 93",
email: "julie@localhost.com",
isFavorite: true,
},
],
};
},
FriendContact.vue
<template>
<li>
<h2>{{ name }} {{ friendIsFavorite ? "(Favorite)" : "" }}</h2>
<button @click="toggleFavorite">Toggle Favorite</button>
<button @click="toggleDetails">
{{ detailsAreVisible ? "Hide" : "Show" }} Details
</button>
<ul v-if="detailsAreVisible">
<li><strong>Phone:</strong>{{ phoneNumber }}</li>
<li><strong>Email:</strong>{{ emailAddress }}</li>
</ul>
</li>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
phoneNumber: {
type: String,
required: true,
},
emailAddress: {
type: String,
required: true,
},
isFavorite: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
detailsAreVisible: false,
friendIsFavorite: this.isFavorite,
};
},
methods: {
toggleDetails() {
this.detailsAreVisible = !this.detailsAreVisible;
},
toggleFavorite() {
this.friendIsFavorite = !this.friendIsFavorite;
},
},
};
</script>
각 친구별로 isFavorite 버튼으로 토글을 할 수는 있지만, FriendContact 컴포넌트 내부에서만 변경이 일어난다.
하지만, 컴포넌트 내부에서 변경하는 것만으로는 부족하다.
현재 isFavorite은 App.vue의 friends 데이터에서 관리되고, FriendContact 컴포넌트로 내려보낼 수 있게 되었다. 하지만 그저 초기 props값처럼 동작한다.
'Toggle Favorite" 버튼을 누르면, FriendContact.vue 컴포넌트 내부에서만 값이 변경되지(심지어 isFavorite값이 변경되는 것도 아니다..), App.vue에서 friends 데이터에서는 변경되지 않는다.
현재는 그냥 dummy data일뿐이지만, 이게 만약 데이터베이스에서 받은 데이터라면, 컴포넌트 내부에서만 값이 바뀌는게 아니라 데이터베이스로 돌려보내야한다.
여기서는, App.vue 파일 내부의 friends 데이터에도 isFavorite값 변경이 적용되어야한다.
그럴려면 FriendContact.vue 컴포넌트(자식) ⇒ App.vue(부모)로 통신해야한다. props와는 반대 방향이다.
props : App.vue(부모) ⇒ FriendContact.vue(자식)로 데이터 전달.
이제는 반대로 FriendContact.vue(자식) ⇒ App.vue(부모)로 isFavorite 상태변경을 전달해야한다.
어떻게 구현할까 ?
# 자식 ⇒ 부모 통신 방법 : $emit
우선, 일반 HTML 요소에서는 어떻게 하는지 살펴보자.
우리가 button을 클릭하면, button은 우리에게 클릭되었음을 알려주려고 한다. 따라서 button은 click 이벤트를 발생(emit) 시킨다.
<button @click=""></button>
1/ 자식 컴포넌트 : $emit으로 커스텀이벤트 발생시킴
Vue의 커스텀 컴포넌트에서도 마찬가지이다.
자식 컴포넌트가 부모에게 무언가 일어났음을 알리고자 한다면, 자식 컴포넌트는 부모가 수신할 이벤트를 발생(emit)시켜야 한다.
Vue 컴포넌트 내부에 우리가 지정한 커스텀 이벤트를 발생시킬 수 있다.
예를 들어, toggleFavorite함수가 실행됐다.
toggleFavorite() {
this.friendIsFavorite = !this.friendIsFavorite;
},
toggleFavorite메서드에 friendIsFavorite data 프로퍼티를 변경하는 대신, Vue가 제공하는 내장 메서드를 사용할 수 있다. → $emit : Vue 컴포넌트 내부에서 this 키워드로 호출할 수 있는 내장메서드.
이 메서드는 부모 컴포넌트에서 수신할 수 있는 커스텀 이벤트를 발생시킨다.
- emit은 최소 하나의 인수인, "커스텀 이벤트"의 이름을 요구한다.
- "커스텀 이벤트"이므로 이름은 원하는대로 지어주면 된다.
- 대신 kebab-case로 쓰는게 관습. (이벤트의 경우, kebab-case만 쓴다.)
toggle-favorite 이벤트를 emit 시켜보자.
toggleFavorite() {
this.$emit('toggle-favorite'); //toggle-favorite이 커스텀 이벤트
},
2/ 부모 컴포넌트 : 커스텀이벤트 수신
자식 컴포넌트가 이 커스텀 이벤트를 emit하면, 부모 컴포넌트는 그걸로 뭘 하면 될까?
→ 버튼 클릭을 수신하는 것처럼 이벤트를 수신해야한다.
부모 컴포넌트인 App.vue 파일에서 커스텀 이벤트를 수신하려면 v-on이나 @를 사용하면 된다.
커스텀 이벤트인 toggle-favorite을 수신하게 하고, 이 이벤트가 발생했을 때 실행되어야 할 JS 코드를 바인딩해준다.
toggle-favorite 이벤트가 발생했을 때, toggleFavoriteStatus 함수가 실행되도록 해보자.
//App.vue
<friend-contact
v-for="friend in friends"
:key="friend.id"
:name="friend.name"
:phone-number="friend.phone"
:email-address="friend.email"
:is-favorite="friend.isFavorite"
@toggle-favorite="toggleFavoriteStatus"
></friend-contact>
methods: {
toggleFavoriteStatus(){}
alert('This works!')
}
'Toggle Favorite' 버튼 클릭 → toggleFavorite 메서드 실행 → 'toggle-favorite' 커스텀 이벤트 발생(emit) → 부모 컴포넌트에서 'toggle-favorite' 커스텀 이벤트에 바인딩된 'toggleFavoriteStatus'메서드 실행 → alert함수가 실행.
이렇게 자식 컴포넌트 → 부모 컴포넌트에게로 통신하는 방법을 알아봤다.
하지만 여기서 끝이 아니다.
커스텀 이벤트를 발생하게만 했지, 데이터를 전달하지 않았다.
2-1/ emit 시, 데이터 전달 방법
부모인 App.vue파일에 두 friends 중, 누구의 contact의 isFavorite 상태가 변경됐는지 알려줘야한다.
현재로서는 누구의 contact인지 알 수 없다.
자식 컴포넌트인 FriendContact.vue에서 emit할 때, 두 번째 인수로 데이터를 전달할 수 있다.
정확하게는, 필요한만큼 몇개든 상관없이 데이터를 전달할 수 있다. 모든 추가 인수는 커스텀 이벤트와 함께 전달될 데이터이다.
emit으로 커스텀 이벤트와 함께 props로 전달받은 id를 같이 emit해보자.
props: {
id: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
phoneNumber: {
type: String,
required: true,
},
emailAddress: {
type: String,
required: true,
},
isFavorite: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
toggleFavorite() {
this.$emit("toggle-favorite", this.id); //props로 전달받은 id
},
},
이제 toggle-favorite 이벤트가 발생하면, 추가 데이터로 이 id도 전달된다.
그러면 이 이벤트를 수신하는 method의 첫 번째 인수로 제공된다.
JavaScript에 내장된 find메서드를 사용하여 friends 데이터에서 id와 friendId가 일치하는 객체를 찾는다.
** Array.find() 함수
find() 메서드 : 주어진 판별 함수를 만족하는 첫 번째 요소의 값을 반환한다. 그런 요소가 없다면 undefined를 반환.
const array1 = [5, 12, 8, 130, 44];
const found = array1.find((element) => element > 10);
console.log(found); // 12
friendId인수와 friends 배열에서 friend.id 프로퍼티 값이 같은 객체를 찾는다.
그리고 그 객체의 isFavorite의 데이터를 변경시킨다.
methods: {
toggleFavoriteStatus(friendId){} //여기서 friendId는 toggle-favorite 커스텀이벤트의 두번째 인수인 this.id값
// 인수이름은 무엇이든 괜찮다.
const identifiedFriend = this.friends.find(friend => friend.id === friendId);
identifiedFriend.isFavorite = !identifiedFriend.isFavorite;
}
3/ 데이터 변경 후, 부모 컴포넌트 ⇒ 자식 컴포넌트 변경된 데이터 props 전달
data가 변경되면, Vue가 해당 변경사항을 감지하고 업데이트된 isFavorite 상태를 자동으로 FriendContact 컴포넌트로 props로 돌려보내 UI를 업데이트한다.
이제 FriendContact.vue 컴포넌트 내부가 아닌, App.vue 파일 내부에서 isFavorite 상태를 변경할 수 있게 되었다.
이렇게 단방향 데이터 플로우(Unidirectional data flow)를 양방향으로 만들었다.
props를 App.vue(부모 컴포넌트)에서 자식컴포넌트로 전달하는데 썼고, $emit 메서드로 컴포넌트 내부에서 커스텀 이벤트를 발생시켜 반대방향으로 전달하는데 썼다.
App.vue에서 커스텀 이벤트를 수신해 메인 앱의 데이터 프로퍼티가 저장된 App.vue의 데이터를 변경했다.
→ 업데이트된 데이터를 컴포넌트로 props로 다시 돌려보냄 → Vue가 변경사항을 확인하고 UI를 업데이트함.
이렇게 컴포넌트 작업을 할 수 있다.
자체적으로 이벤트를 발생시키고, 데이터를 설정할 수 있다.
** 출처: 모든 내용은 Udemy Vue-완벽가이드 강의를 기반으로 작성하였습니다.
'Vue.js' 카테고리의 다른 글
[Udemy Vue 완벽가이드 Section8] 100. 프로퍼티/이벤트 폴스루 및 모든 프로퍼티 바인딩하기 (0) | 2023.08.25 |
---|---|
[Udemy Vue 완벽가이드 Section8] 99. 커스텀 이벤트 정의 및 검증하기 (0) | 2023.08.25 |
[Udemy Vue 완벽가이드 Section8] 97. 동적 props값 사용하기 (0) | 2023.08.24 |
[Udemy Vue 완벽가이드 Section8] 96. 지원되는 props 값 (0) | 2023.08.24 |
[Udemy Vue 완벽가이드 Section8] 95. props 검증하기 (0) | 2023.08.24 |