본문 바로가기
프론트엔드/Vue

[Vue.js] 뷰 컴포넌트간 양방향 데이터 바인딩

by jinwanseo 2021. 8. 14.
728x90

[Vue.js] 뷰 자식 컴포넌트와 부모컴포넌트 데이터 양방향 바인딩

⭐️  학습 목표

props를 통해 부모-> 자식 컴포넌트로 데이터를

전달했다면 자식 컴포넌트에서 이벤트 발생시

부모 컴포넌트로 데이터를 다시 바인딩 해야하는 경우도 있는데

아래 예제를 통해 이를 학습해보도록 하자

 

⭐️  기본 형태

<!--부모 컴포넌트에서 자식 컴포넌트로 데이터 전달-->
<template>
    <!--여기는 부모 컴포넌트임-->
    <자식컴포넌트명 :props명="데이터명"/>
</template>

<!--자식 컴포넌트에서 버튼 클릭시 부모 컴포넌트로 데이터 전달-->
<!--자식 에서 부모 컴포넌트로 데이터 및 상태 전달시 $emit()을 사용한다-->
<template>
    <!--여기는 자식 컴포넌트임-->
    <button @click="$emit('gogoData','123')">부모에게 데이터 전송!</button>
</template>

<!--부모 컴포넌트에서 자식 컴포넌트에서 보낸 데이터 처리-->
<!--자식 컴포넌트에서 보낸 데이터는 $event라는 변수를 통해 전달받는다-->
<!--자식 컴포넌트에서 보낸 123 이라는 문자열을 출력-->
<template>
    <!--여기는 부모 컴포넌트임-->
    <자식컴포넌트명 @gogoData="console.log($event);">
</template>

 

⭐️  샘플 예제 (로직)

메뉴와 상품 리스트가 나오는 예제인데

부모 컴포넌트는 App, 자식 컴포넌트는 Menu, Product,Modal이다

 

자식 컴포넌트 내 상품 제목을 클릭하면 모달창이 출력되고

닫기 버튼을 클릭시 모달창이 종료되는 로직인데

자식 컴포넌트인 상품 컴포넌트에서 제목 클릭 이벤트 발생시

상품의 고유번호와 모달창 닫힘 상태를 부모 컴포넌트로 전달하고

부모 컴포넌트에서 해당 상태를 다시 자식 컴포넌트인

Modal 컴포넌트에 전달하는 예제이다

 

⭐️   App Component (부모 컴포넌트)

<!--UI-->
<template>
  <Modal :product="products[stateId]" :isAct="isModalAct" @closeModal="closeModal"/>
  <Menu :menu="menu"/>
  <nav>
    <ul>
      <Product v-for="product,i in products" :key="i" v-bind:product="product" @openModal="openModal($event)"/>
    </ul>
  </nav>
</template>

<!--Script-->
<script>
import Menu from './components/Menu.vue';
import Product from './components/Product.vue';
import Modal from './components/Modal.vue';

export default {
  name : 'App',
  data (){
    return {
      stateId : 0,
      isModalAct : false,
      menu : ['HOME','ABOUT' ,'PRODUCTS', 'ETC'],
      products : [
        {id : '0', title : 'Sample Product1', price : 10000, img : 'https://dummyimage.com/200/F6A9A9/464660'},
        {id : '1', title : 'Sample Product2', price : 50000, img : 'https://dummyimage.com/200/FFBF86/464660'},
        {id : '2', title : 'Sample Product3', price : 30000, img : 'https://dummyimage.com/200/FFF47D/464660'},
        {id : '3', title : 'Sample Product5', price : 70000, img : 'https://dummyimage.com/200/C2F784/464660'},
      ]
    }
  },
  methods : {
    openModal(id){
      this.stateId = id;
      this.isModalAct = true;
    },
    closeModal(){
      this.stateId = 0;
      this.isModalAct = false;
    }
  },
  components : {
    //ES6 부터 key, value의 변수명이 같을때 생략이 가능하다
    //Menu : Menu, Product : Product, Modal : Modal
    Menu,Product,Modal
  }
}
</script>

<!--CSS-->
<style>

</style>

 

⭐️  Menu Component (자식 컴포넌트-메뉴)

<template>
  <nav>
    <ul class="menu">
      <li class="menu--item" v-for="item,i in menu" :key="i">
        <a :href="'/'+item">{{item}}</a>
      </li>
    </ul>
  </nav>
</template>

<script>
export default {
  name : 'Menu',
  props : {
    menu : Array
  } 
}
</script>

<style>
  .menu {
    display : flex;
    justify-content: center;
    padding : 10px;
    border-radius: 5px;
    background: steelblue;
    list-style: none;
  }

  .menu--item {
    padding : 10px;
  }

  .menu--item a {
    color : white;
    text-decoration: none;
  }
</style>

 

⭐️  Product Component (자식 컴포넌트-상품)

<template>
  <li class="product">
      <img :src="product.img"/>
      <div class="product--content">
        <h3 class="content--title" @click="openModal(product.id)">{{product.title}}</h3>
        <div class="content--price">상품가 : {{product.price}} 원</div>
      </div>
  </li>
</template>

<script>
export default {
    name : 'Product',
    props : {
        product : Object
    },
    methods : {
        openModal(id){
            this.$emit('openModal',id);
        }
    }
}
</script>

<style>
    .product {
        display : flex;
        flex-direction: row;
        justify-content: center;
        flex-wrap: wrap;
        align-items: center;
        list-style: none;
        margin : 10px;
    }

    .product--content { 
        margin-left : 10px;
    }

    .content--title {
        cursor : pointer;
    }
</style>

 

⭐️  Modal Component (자식 컴포넌트-모달)

<template>
  <div class="modal--bg" v-if="isAct">
      <div class="modal--fg">
          <img :src="product.img"/>
          <h3>{{product.title}}</h3>
          <div>상품가 : {{product.price}} 원</div>
          <button class="modal--fg--btn" @click="closeModal">닫기</button>
      </div>
  </div>
</template>

<script>
export default {
    name : 'Modal',
    methods : {
        closeModal(){
            this.$emit('closeModal');
        }
    },
    props : {
        product : Object,
        isAct : Boolean
    }
}
</script>

<style>
    .modal--bg {
        display: flex;
        position: fixed;
        flex-direction: column;
        align-items: center;
        background-color: rgba(0, 0, 0, 0.6);
        width : 100vw;
        height: 100vh;
    }

    .modal--fg {
        display : flex;
        position: absolute;
        border-radius: 10px;
        padding : 20px;
        top : 10%;
        flex-direction: column;
        align-items: center;
        background-color: whitesmoke;
        width : 80%;
        max-width: 450px;
        user-select: none;
    }

    .modal--fg--btn {
        margin-top : 10px;
        padding : 6px 12px;
        font-size : 19px;
    }
</style>

 

🌈  출력 결과

728x90

댓글