국비교육(22-23)

40일차(1) : Vue.js 예제(4)

서리/Seori 2022. 12. 4. 23:54

40일차(1) : Vue.js 예제(4)

 

- Vue.js로 이벤트 발생시키기

 

Step08_eventEmit

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Step08_eventEmit.html</title>
</head>
<body>
    <h1>자식 component 에서 발생하는 이벤트</h1>
    <div id="app">
        <my-component v-on:mom="feed"></my-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component("my-component", {
            template:`
                <div>
                    <button v-on:click="callMom">엄마!</button>    
                </div>
            `,
            methods:{
                callMom(){
                    // this.$emit("이벤트명", 전달할 data)
                    this.$emit("mom", "배고파");
                }
            }
        });

        let app=new Vue({
            el:"#app",
            methods:{
                feed(data){
                    alert(data);
                }
            }
        });
    </script>
</body>
</html>

 

- root component 는 자식 component를 가질 수 있다.

- 자식 component를 분리한다는 것은 어떤 작업을 나누어 분업화한다는 면에서 의미가 있다.

- root component 는 전체를 조율하는 개념이다.

 

 

- 자식 component 안에서 어떤 이벤트가 발생할 수 있다.(drag, submit, click, mousemove, ...)

 

- 보통은 자식 component 안에서 해결하지만,

 때로는 자식 component에서 발생하는 이벤트를 부모 component에 알려야 할 수 있다.

- 부모로부터 데이터를 받아서 사용한다거나 하는 경우!

 

ex) 1페이지를 부모로부터 받아서 사용자에게 보여주고 있는데 사용자가 2페이지를 클릭하는 경우.

 부모 component에 2페이지의 데이터가 필요하다고 요청하기

자식-부모 간의 소통이 필요한 경우가 있다. 이런 경우에 사용하는 것이 이벤트 발생시키기!

 

v-on:mom

-  자식 → 부모 이벤트 발생시키기

 

 

- 어떤 버튼이 있다고 예시를 들면

 button v-on:click="clicked" : 이 버튼에 클릭 이벤트가 일어나면 " " 함수를 호출할 것이라는 뜻이다.

 

- <my-component v-on:mom="feed"> 는

  mom이라는 이벤트가 일어나면 feed함수를 호출할 것이라는 의미가 된다.

 

- 이 컴포넌트에 어떻게 mom이라는 이벤트가 발생할까?

 

- 아래에 this.$emit 으로 지정해둔다.

- 이벤트명은 마음대로 지정할 수 있고, 전달할 인자가 있으면 같이 전달하면 된다.

- '엄마' 버튼을 누르면 이벤트 발생. this.$emit 로 이벤트를 발생시키는 것이다.

 

 

- 클릭하면 methods 안에 들어있는 mom-feed함수가 실행된다.

method의 feed함수를 발생시키면서 인자도 함께 전달한다.

 

- 버튼 클릭시 alert(data) 가 실행된다.

 


 

Step08_eventEmit2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Step08_eventEmit2.html</title>
</head>
<body>
    <h1>event emit 예제</h1>
    <div id="app">
        <friend-component 
            v-bind:list="members"
            v-on:delete="deleteMember"></friend-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component("friend-component",{
            template:`
                <ul>
                    <li v-for="(item, index) in list">
                        {{item}}
                        <button v-on:click="deleteItem(index)">삭제</button>
                    </li>
                </ul>    
            `,
            props:["list"],
            methods:{
                deleteItem(i){
                    this.$emit("delete", i);
                }
            }
        });

        let app=new Vue({
            el:"#app",
            data:{
                members:['바나나','딸기','복숭아']
            },
            methods:{
                deleteMember(index){
                	//members 모델(배열) 에서 index 에 해당하는 아이템 1개 삭제
                    this.members.splice(index, 1);
                }
            }
        });
    </script>
</body>
</html>

 

 

- "list" property 를 받고, "delete" 이벤트를 발생시킨다.

 

- Component 안에 list라는 property를 갖고있으니까 list를 바인딩할 수 있고,

 delete라는 이벤트를 가지고 있으니까 delete 해당 이벤트에 대해서 on: 할 수 있는 것이다.

 

- list 를 전달받아서 for문을 돌면서 li를 여러개 만들어낸다. 배열[ ] (빨간 화살표)

- 배열의 아이템 인덱스를 얻어내면서 index 를 deleteItem 함수에 전달하고,

 이벤트를 발생시키면서 부모 component에 전달한다. (분홍 화살표)

 

- 자식 component 에서 버튼을 누르면 삭제되는데, 삭제되는 이유는

 delete 이벤트를 발생시키면서 부모 component에 데이터를 전달하면,

 delete 이벤트가 일어나면서 deleteMember 함수를 호출한다.

 이 deletemember(){} 함수가 실행되면서 삭제도 되고, index값도 전달되는 것이다.

 

- 부모component 입장에서 보면 배열에 변화를 가하는 것이다.

 삭제하기만 하면 자식 component에 자동으로 전달된다.

- 부모가 가진 모델의 원본을 수정하는 것만으로도 자식에게 그 값이 전달된다.

 


 

Step08_eventEmit3 - 수정 기능도 있다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Step08_eventEmit3.html</title>
</head>
<body>
    <h1>event emit 예제</h1>
    <div id="app">
        <friend-component 
            v-bind:list="members"
            v-on:delete="deleteMember"
            v-on:update="updateMember"></friend-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component("friend-component",{
            template:`
                <ul>
                    <li v-for="(item, index) in list">
                        {{item}}
                        <button v-on:click="updateItem(index)">수정</button>
                        <button v-on:click="deleteItem(index)">삭제</button>
                    </li>
                </ul>    
            `,
            props:["list"],
            methods:{
                deleteItem(i){
                    this.$emit("delete", i);
                },
                updateItem(i){
                    const newName=prompt("수정할 이름을 입력하세요");

                    //this.$emit("update", {i:i, newName:newName});
                    this.$emit("update", {i, newName});
                }
            }
        });

        let app=new Vue({
            el:"#app",
            data:{
                members:['바나나','딸기','복숭아']
            },
            methods:{
                deleteMember(index){
                    this.members.splice(index, 1);
                },
                updateMember(data){
                    //아래처럼 배열을 변경하면 변경이 감지가 안되기 때문에 화면 업데이트가 안된다.
                    //this.members[data.i] = data.newName;

                    //아래의 2가지 방법중 하나로 배열을 변경해야 한다. 
                    //Vue.set(this.members, data.i, data.newName);
                    // $set(수정할배열의참조값, 수정할 인덱스, 수정할 값)
                    this.$set(this.members, data.i, data.newName);
                }
            }
        });
    </script>
</body>
</html>

 

- 부모 component 가 갖고 있는 members 배열 전달

- 이벤트 발생시 delete, update 함수 실행

 

 

this.$emit("update", {i, newName});

- 이름(newName)을 입력받아서 

 update라는 함수를 발생시키면서 두개의 값을 전달한다.(i값과 newName)

 

- 그런데 object의 값이 좀 특이하다.

- 위와 같이 {i:i, newName:newName} 으로 작성해야하는데, {i, newName} 으로만 적는다.

 

- 보통 object의 방의 이름을 지역변수의 이름과 일치시켜 사용하는 경우가 많다.

 

 

- 그래서 방의 이름은 지역변수 그대로 하고, 지역변수의 값을 value로 하여 { 변수명, 변수명 } 으로만 써도

 이렇게만 써도 알아서 {변수명1:value1, 변수명2:value2}로 object를 만들어준다!

 

- update 이벤트가 들어가면, 이 위치에는 object가 전달된다.

 

 


 

참고) Vue에서 배열 모델을 사용하는 경우의 주의사항

 


- 위와 같은 경우 수정으로 간주되므로 UI는 자동으로 업데이트된다.

 


[ Vue에서 배열을 모델로 사용하는 경우 ] 

- 배열에 저장된 아이템을 반복문 돌면서 참조해서 여러개의 UI를 만들어낸다.
- 배열이 수정되면, UI는 자동으로 update된다. → 꼭 그런것은 아니다.

 

- 여기서 수정의 기준이 애매하다. 배열을 수정했을 때 수정이라고 간주되는 것이 있고 아닌 것이 있다.

- 배열의 특정 인덱스를 삭제해서 배열의 사이즈가 변경된 경우 수정O (업데이트된다)
- 새로운 배열의 참조값으로 대체(덮어쓰기) → 수정O (업데이트된다)
- 배열의 특정 인덱스에 저장된 아이템을 다른 아이템으로 대체하는 경우 수정X

 

 

- 이 안의 어디에선가 this.members=[10,20,30] 로 값을 바꾸어 넣을 수 있다. 이것은 수정으로 간주된다.

- 수정으로간주되므로 UI는 자동으로 업데이트된다.

 

let nums=[10,20,30]
nums[1]=999;

- 하지만 위와 같이 배열의 특정 인덱스에 저장된 아이템을 다른 아이템으로 대체하는 경우에는

 배열이 변경되었다고 간주하지 않기 때문에, 추가적인 작업이 필요한 것!

 

- 이 경우 배열의 값을 바꿨다고해서 UI가 바로 업데이트 되는 것은 아니다.

- 배열을 수정하고 업데이트도 하게 하고 싶다면 this.$set 을 쓰는 것이다.

 

 

Vue.set(this.members, data.i, data.newName);
$set(수정할 배열의 참조값, 수정할 인덱스, 수정할 값)

 

- 이 경우 둘 중 하나를 작성해줌으로써 배열도 수정되고 UI도 업데이트되도록 해주어야 한다.