본문 바로가기
SPA/Vue

vue.js Todolist 만들기 [컴포넌트 통신O] --2

by memeseo 2021. 5. 7.

컴포넌트 통신하지 않은 Todolist 보러 가기 ▼

solm-blog.tistory.com/11

 

vue.js ToDoList 만들기 [컴포넌트 통신X]

-Todolist 제작 header, input, list, footer 네 가지의 컴포넌트로 나누어 제작 -기능 설명 input : list 입력, list없을 시 작성 안내 문구, 입력칸이 공백인데 등록 버튼 클릭 시 작성 안내 모달창 출력 list :..

solm-blog.tistory.com

전에 올린 포스팅은 컴포넌트간 통신이 되지않아 데이터 교류가 되지 않는 오류가 있었다.

오류를 해결하기 위해 v-bind v-on을 이용하여 컴포넌트간 통신이 이루어지게 해보자.

 


 

컴포넌트 통신

*주석으로 달아놓은 번호 따라가면 컴포넌트 통신 큰 줄기 이해 가능

 

 

1. App.vue

<template>
  <div id="app">
    <Header></Header>
    <Input v-on:addTodoItems="addOneItem"></Input>
    <!-- 3. v-bind:내려보낼 props 속성 이름="현재 위치의 컴포넌트 데이터 속성" -->
    <List v-bind:propsdata="todoItems" v-on:removeOneItem="removeItem"></List>
    <Footer v-on:removeAllItems="clearAllItems"></Footer>
  </div>
</template>

<script>
import Header from './components/Header'
import Input from './components/Input'
import List from './components/List'
import Footer from './components/Footer'



export default {

  data(){
    return{
      // 2. 모든 components에서 사용되는 데이터 정의
      todoItems : []
    }
  },

 //1. life cycle중에서 가장 처음 실행되는 creation단계의 hook을 list.vue에서 App.vue로 옮겨옴
 created(){
        if(localStorage.length > 0){
            for(let i=0; i < localStorage.length; i++){
                if(localStorage.key(i) !== "loglevel:webpack-dev-server"){
                    this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
                }
            }
        }
    },

    methods : {
      removeItem(todoItem, index){
            localStorage.removeItem(todoItem.title);
            this.todoItems.splice(index, 1);
        },

      addOneItem(Item){
        const todoItem = {
             title : Item,
             completed : false
         }

         localStorage.setItem(todoItem.title, JSON.stringify(todoItem));
         this.todoItems.push(todoItem); // 추가
      },

      clearAllItems(){
        localStorage.clear();
        
        //localStorage도 삭제해주고 배열도 삭제해줘야함 *
        this.todoItems = [];
      }

    },

  
  components : {
    Header,
    Input,
    List,
    Footer
  }
}
</script>

<style>

  html body {
    width : 100%;
    height : 100%;
    text-align : center;
   
  }

  #app {
    margin : 0 auto;
    padding : 1%;
    width : 40%;
    border : 1px solid black;
  }



</style>

- v-bind:내려보낼 프롭스 속성 이름="현재 위치의 컴포넌트 데이터 속성"

: 오타주의, 적용되는 속성 위치 파악 중요

 

- Item 추가 (addOneItem method)

: 컴포넌트간 데이터 통신이 목적이기 때문에 localStorage에 Item을 추가해주는 코드만 App.vue로 이동

 

- List 전체 삭제 (clearAllItems method)

: localStorage 데이터만 삭제하는 게 아닌 배열도 비워줘야 view에서 리스트가 삭제된게 확인됨

 

 

2. Input.vue

<template>
  <div>
      <input type="text" v-model="newTodoItem" @keyup.enter="addTodoItem"/><button @click="addTodoItem">click</button>
      <modal v-if="showModal" @close="showModal=false">
          <template v-slot:header><i class="closeModal fas fa-window-close" @click="closeModalBtn"></i></template>
          <template v-slot:body>List를 입력해주세요!</template>
      </modal>
  </div>
 
</template>

<script>
import modal from './modal'


export default {
 data(){
     return {
         newTodoItem : "",
         showModal : false
     }
 },

 methods : {
     addTodoItem(){
         if(this.newTodoItem !== ""){
         this.$emit('addTodoItems', this.newTodoItem)
         this.itemClear();
         }else{
             this.showModal = !this.showModal;
         }
     },

     itemClear(){
         this.newTodoItem = "";
     },

     closeModalBtn(){
         this.showModal = !this.showModal;
     }

 },

 components : {
     modal
 }

  
}
</script>

<style>
.closeModal{
    padding-left : 98%;

}
</style>

 

3. List.vue

<template>
  <div id="listApp">

    <!-- fontawesome CDN-->
      <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css"
  />
    
    <!-- list 출력 -->
      <div v-if="this.propsdata.length <= 0" class="ListIsEmpty"> 작성된 List가 없습니다. <br> List를 작성해주세요. </div>
      <!-- 5. 기존에 todoItems를 propsdata로 바꿔줌 끝~!-->
      <ul v-for="(todoItem, index) in propsdata" :key="todoItem.title" class="ShowTodoList">
         <li><i class="fas fa-check" @click="toggleBtn(todoItem, index)" :class="{finishToo : todoItem.completed}"></i></li>
         <li :class="{finish : todoItem.completed}">{{todoItem.title}}</li>
         <li><i class="fas fa-trash-alt" @click="OneTodoItemDel(todoItem, index)" ></i></li>
         <br>
      </ul>
      
  </div>
</template>

<script>

export default {
    // 4. vue.js에서 propsdata 전달받음
    props : ['propsdata'],
    methods : {
        OneTodoItemDel(todoItem, index){
           this.$emit('removeOneItem', todoItem, index);
        },

        toggleBtn(todoItem){
            todoItem.completed = !todoItem.completed;
            localStorage.removeItem(todoItem.title);
            localStorage.setItem(todoItem.title, JSON.stringify(todoItem));
        }

    }




}
</script>

<style>
    .ShowTodoList{
        margin : 0 auto;
        padding : 2%;
        text-align : left;
    }

    .ListIsEmpty{
        font-size : 0.6rem;
        line-height : 20px;
        color : gray;
        padding-top : 40px;
        padding-bottom : 30px;
        
    }

    .finishToo{
        color : gray;
    }

    .finish{
        text-decoration: line-through;
        color : gray;
    }

    .listApp{
        width : 100%
    }

    .ShowTodoList {
        width : 70%;
    }

    .ShowTodoList > li{
       list-style: none;
       float : left;
    }

    .ShowTodoList > li:nth-child(1){
        width : 10%;
    }

    .ShowTodoList > li:nth-child(2){
        width : 80%;
    }

    .ShowTodoList > li:nth-child(3){
        width : 10%;
    }

</style>

 

4. Footer.vue

<template>
  <div>
    <button @click="allRomove" class="todoAllRemove"> 전체 삭제 </button>
  </div>
</template>

<script>

export default {
 methods : {
   allRomove(){
      this.$emit('removeAllItems');
   }
 }
  
}
</script>

<style>
  .todoAllRemove{
    border : none;
    border-radius : 10px;
    padding : 6px;
    font-size : 0.8rem;

  }
</style>

*Header.vue는 코드 변화가 없어 첨부하지 않음