Todo App

컴포넌트 생성 및 등록

  1. todoapp 프로젝트 생성
npm install -g @vue/cli
vue create todoapp
cd todoapp
npm run serve
  1. 'TodoHeader.vue', 'TodoInput.vue', 'TodoList.vue', 'TodoFooter.vue' 파일 생성
|- /public
    |- favicon.ico
    |- index.html
|- /src
    |- /components
        + |- /TodoHeader.vue
        + |- /TodoInput.vue
        + |- /TodoList.vue
        + |- /TodoFooter.vue
    |- App.vue
    |- main.js
  1. 'App.vue' 수정
/* App.vue */
<template>
  <div id="app">
    <TodoHeader></TodoHeader>
    <TodoInput></TodoInput>
    <TodoList></TodoList>
    <TodoFooter></TodoFooter>
  </div>
</template>

<script>
import TodoHeader from './components/TodoHeader.vue';
import TodoInput from './components/TodoInput.vue';
import TodoList from './components/TodoList.vue';
import TodoFooter from './components/TodoFooter.vue';

export default {
  components:{
    'TodoHeader': TodoHeader,
    'TodoInput' : TodoInput,
    'TodoList' : TodoList,
    'TodoFooter' : TodoFooter
  }
}
</script>

<style>
body{text-align:center;background:#f5f5f5;font-family: 'Ubuntu', sans-serif;}
input{border-style:groove;width:200px;}
button{border-style:groove;}
.shadow{box-shadow:5px 10px 10px rgba(0,0,0,0.03);}
</style>

파비콘, 아이콘, 폰트, 반응형 태그 설정

파비콘 생성

favicon generator

웹폰트 설정

fontawesome google ubuntu

<!-- index.html -->
<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">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>todoapp</title>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    <link href="https://fonts.googleapis.com/css?family=Ubuntu" rel="stylesheet">
</head>

TodoHeader 컴포넌트 구현

/* TodoHeader.vue */
<template>
    <header>
        <h1>TODO</h1>
    </header>
</template>

<style scoped>
h1{color:#2f3b52;font-weight:900;margin:2.5rem 0 1.5rem;}
</style>

TodoInput 컴포넌트 구현

/* TodoInput.vue */
<template>
    <div class="inputBox shadow">
        <input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
        <span class="addContainer" v-on:click="addTodo">
            <i class="fas fa-plus addBtn"></i>
        </span>
    </div>
</template>

<script>
export default {
    data:function(){
        return {
            newTodoItem: ""
        }
    },
    methods:{
        addTodo:function(){
            if(this.newTodoItem !== ''){
                var obj = {completed:false, item: this.newTodoItem};
                localStorage.setItem(this.newTodoItem, JSON.stringify(obj));
                this.clearInput();            
            }            
        },
        clearInput:function(){
            this.newTodoItem = "";
        }
    }
}
</script>

<style scoped>
input:focus{outline:none;}
.inputBox{background:#fff;height:50px;line-height:50px;border-radius:5px;}
.inputBox input{border-style:none;font-size:0.9rem;}
.addContainer{float:right;background:linear-gradient(to right, #6478FB, #8763FB);display:block;width:3rem;border-radius:0 5px 5px 0;}
.addBtn{color:#fff;vertical-align:middle;}
</style>

TodoList 컴포넌트 구현

/* TodoList.vue */
<template>
    <div>
        <ul>
            <li v-for="(todoItem, index) in todoItems" v-bind:key="todoItem.item" class="shadow">
                <i class="checkBtn fas fa-check" v-bind:class="{checkBtnCompleted: todoItem.completed}" v-on:click="toggleComplete(todoItem, index)"></i>
                <span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
                <span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
                    <i class="fas fa-trash-alt"></i>
                </span>
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    data:function(){
        return {
            todoItems: []
        }
    },
    methods:{
        removeTodo:function(todoItem, index){
            localStorage.removeItem(todoItem);
            this.todoItems.splice(index,1);
        },
        toggleComplete:function(todoItem, index){
            todoItem.completed = !todoItem.completed;
            localStorage.removeItem(todoItem.item);
            localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
        }
    },
    created:function(){ //인스턴스가 생성되자마자 호출
        if(localStorage.length > 0){
            for(var i=0; i<localStorage.length;i++){
                if(localStorage.key(i) !== "loglevel:webpack-dev-server"){
                    this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));                
                }                
            }
        }
    }
}
</script>

<style scoped>
ul{list-style-type:none;padding-left:0px;margin-top:0;text-align:left;}
li{display:flex;min-height:50px;height:50px;line-height:50px;margin:0.5rem 0;padding:0 0.9rem;background:#fff;border-radius:5px;}
.checkBtn{line-height:45px;color:#62acde;margin-right:5px;}
.checkBtnCompleted{color:#b3adad;}
.textCompleted{text-decoration:line-through;color:#b3adad;}
.removeBtn{margin-left:auto;color:#de4343;}
</style>

TodoFooter 컴포넌트 구현

/* TodoFooter.vue */
<template>
    <div class="clearAllContainer">
        <span class="clearAllBtn" v-on:click="clearTodo">clear all</span>
    </div>
</template>

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

<style scoped>
.clearAllContainer{width:8.5rem;height:50px;line-height:50px;background-color:#fff;border-radius:5px;margin:0 auto;}
.clearAllBtn{color:#e20303;display:block;}
</style>