TODO 예제 – 4. 문제점 해결

영역별로 나눠져 있는 todoItem을 app에서 관리하도록 해보겠습니다.
propsdata를 사용합니다.

1.컴포넌트들과 바인딩

App.vue
<template>
    <div id="app">
        <TodoHeader></TodoHeader>
        <TodoInput v-on:addTodo="addTodo"></TodoInput>
        <TodoList v-bind:propsdata="todoItems" @removeTodo="removeTodo"></TodoList>
        <TodoFooter v-on:removeAll="clearAll"></TodoFooter>
    </div>
</template>

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

export default {
    data() {
        return {
            todoItems : []
        }
    },
    created() {
        if (localStorage.length > 0) {
            for (var i=0;i <localStorage.length; i++) {
                this.todoItems.push(localStorage.key(i));
            }
        }
    },
    methods : {
        addTodo(todoItem) {
            localStorage.setItem(todoItem,todoItem);
            this.todoItems.push(todoItem);
        },
        clearAll() {
            localStorage.clear();
            this.todoItems = [];
        },
        removeTodo(todoItem,index) {
            localStorage.removeItem(todoItem);
            this.todoItems.splice(index,1);
        }
    },
    components : {
        'TodoHeader' : TodoHeader,
        'TodoInput' : TodoInput,
        'TodoList' : TodoList,
        'TodoFooter' : TodoFooter,
    }
}
</script>

<style>
    body {text-align:center;background-color:#f6f6f8}
    input {border-style:groove;width:200px}
    button {border-style:groove}
    .shadow {box-shadow:5px 10px 10px rgba(0,0,0,0.03)}
</style>

템플릿 영역에서는 하위 컴포넌트에 사용할 데이터와 이벤트를 바인딩 처리합니다.
스크립트 영역에서는 기본적인 메소드들을 선언해주면 이전과 마찬가지로 아이템들을 추가삭제 하도록 합니다.

2.Input 박스처리

TodoInput.vue
<template>
    <div class="inputBox shadow">
        <input type="text" v-model="newTodoItem" placeholder="Type what you have to do" v-on:keyup.enter="addTodo">
        <span class="addContainer" v-on:click="addTodo">
            <i class="addBtn fa fa-plus" aria-hidden="true"></i>
        </span>
    </div>
</template>

<script>
export default {
    data() {
        return {
            newTodoItem : ''
        }
    },
    methods : {
        addTodo() {
            if (this.newTodoItem !== "") {
                var value = this.newTodoItem && this.newTodoItem.trim();
                this.$emit('addTodo',value);
                this.clearInput();
            }
        },
        clearInput() {
            this.newTodoItem = '';
        }
    }
}
</script>


<style>
    input:focus {ouline:none}
    .inputBox {background:white;height:50px;line-height:50px;border-radius:5px}
    .inputBox input {border-style:none;font-size:0.9rem}
    .addContainer {float:right;background:#0080cc;display:inline-block;width:3rem;border-radius:0 5px 5px 0}
    .addBtn {color:#fff;vertical-align:middle}
</style>

스크립트 영역에서 $emit을 사용하여 상위 컴포넌트의 해당이름을 가진 이벤트를 실행합니다.
상위 컴포넌트에서는 addTodo라는 이벤트가 실행되고 매개변수로 입력된 값이 전달됩니다.

3.List 처리

TodoList.vue
<template>
    <section>
        <ul>
            <li v-for="(todoItem,index) in propsdata" class="shadow">
                <i class="checkBtn fa fa-check" aria-hidden="true"></i>
                {{todoItem}}
                <span class="removeBtn" type="button" @click="removeTodo(todoItem,index)">
                    <i class="fa fa-trash-o"></i>
                </span>
            </li>
        </ul>
    </section>
</template>

<script>
export default {
    props : ['propsdata'],
    methods : {
        removeTodo(todoItem,index) {
            this.$emit('removeTodo',todoItem,index);
        }
    }
}
</script>

<style>
    ul {padding:0;list-style:none}
    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}
    .removeBtn {margin-left:auto;color:#de4343}
</style>

템플릿 영역에서 이전에는 해당 컴포넌트에만 선언되어 있던 todoItems를 받아왔지만, 수정뒤에는 props로 받은 데이터를 받아옵니다.
추가로 삭제하는 메소드도 $emit으로 처리합니다.

4.Footer 처리

TodoFooter.vue
<template>
    <div class="clearAllContainer">
        <span class="clearAllBtn" @click="clearTodo">Clear All</span>
    </div>
</template>

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

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

푸터에 있는 전체 삭제 버튼도 $emit을 활용하여 처리합니다.

완성된 모습

complete

종합하자면 Input과 List에 나눠져 있는 todoItems를 App.vue에 선언하고 props를 통해
데이터와 이벤트를 전달하고 컨트롤 하는걸로 변경하였습니다.
위 작업을 통해 할일을 삭제하거나 새로 추가해도 새로고침없이 바로 확인이 가능합니다.