본문 바로가기
SPA/Vue

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

by memeseo 2021. 5. 7.

 

-Todolist 제작

header, input, list, footer 네 가지의 컴포넌트로 나누어 제작

 


 

-기능 설명

input : list 입력, list없을 시 작성 안내 문구, 입력칸이 공백인데 등록 버튼 클릭 시 작성 안내 모달창 출력

list : input으로 받은 list 출력, 할일 목록 진행중/완료 체크 기능, 일정 삭제 기능

footer : list 전체 삭제

 

 

-현재 문제점

1. list 입력 시 바로 적용되지 않고 새로 고침 후 화면에 일정이 등록됨

2. list 삭제 버튼 클릭 시 바로 적용이 안되고 새로 고침 후 삭제 확인 됨

2. list 전체 삭제 버튼 클릭 시 바로 적용되지 않고 새로 고침 후 삭제 확인 됨

=> 각 컴포넌트간에 데이터 전달이 안돼서 갱신이 안됨

 

 

-해결 방법

전체 컴포넌트에서 하나의 데이터만 바라보게 변경

 

 

디자인 무시 부탁 ..

 

 


 

1. App.vue

<template>
  <div id="app">
    <Header></Header>
    <Input></Input>
    <List></List>
    <Footer></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 {
 
  
  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>

 

2. Header.vue

<template>
  <header>TO DO LIST!</header>
</template>

<script>

export default {
 
  
}
</script>

<style>

</style>

 

3. 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 !== ""){
         const todoItem = {
             title : this.newTodoItem,
             completed : false
         }

         localStorage.setItem(todoItem.title, JSON.stringify(todoItem));
         this.itemClear();
         }else{
             this.showModal = !this.showModal;
         }
     },

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

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

 },

 components : {
     modal
 }

  
}
</script>

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

}
</style>

 

4. Modal.vue

<template>
     <transition name="modal">
        <div class="modal-mask">
          <div class="modal-wrapper">
            <div class="modal-container">

                <!--모달헤더-->
              <div class="modal-header">
                <slot name="header">
                  
                </slot>
              </div>
                <!-- 모달바디 -->
              <div class="modal-body">
                <slot name="body">
                  
                </slot>
              </div>
              
            </div>
          </div>
        </div>
      </transition>
</template>

<script>
    export default {

    }

</script>

<style>



    .modal-mask {
  position: fixed;
  z-index: 9998;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: table;
  transition: opacity 0.3s ease;
}

.modal-wrapper {
  display: table-cell;
  vertical-align: middle;
}

.modal-container {
  width: 300px;
  margin: 0px auto;
  padding: 20px 30px;
  background-color: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
  transition: all 0.3s ease;
  font-family: Helvetica, Arial, sans-serif;
}

.modal-header h3 {
  margin-top: 0;
  color: #42b983;
}

.modal-body {
  margin: 20px 0;
  padding-bottom : 5%;
}

.modal-default-button {
  float: right;
}

/*
 * The following styles are auto-applied to elements with
 * transition="modal" when their visibility is toggled
 * by Vue.js.
 *
 * You can easily play with the modal transition by editing
 * these styles.
 */

.modal-enter {
  opacity: 0;
}

.modal-leave-active {
  opacity: 0;
}

.modal-enter .modal-container,
.modal-leave-active .modal-container {
  -webkit-transform: scale(1.1);
  transform: scale(1.1);
}
</style>

 

5. 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="todoItems.length <= 0" class="ListIsEmpty"> 작성된 List가 없습니다. <br> List를 작성해주세요. </div>
      <ul v-for="(todoItem, index) in todoItems" :key="todoItem" 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)" ></i></li>
         <br>
      </ul>
      
  </div>
</template>

<script>

export default {
    data(){
        return {
            todoItems : []
        }

    },

    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 : {
        OneTodoItemDel(todoItem, index){
            localStorage.removeItem(todoItem.title);
            this.todoItems.splice(index, 1);
        },

        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>

 

6. Footer.vue

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

<script>

export default {
 methods : {
   allRomove(){
     localStorage.clear();
   }
 }
  
}
</script>

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