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을 활용하여 처리합니다.
완성된 모습
종합하자면 Input과 List에 나눠져 있는 todoItems를 App.vue에 선언하고 props를 통해
데이터와 이벤트를 전달하고 컨트롤 하는걸로 변경하였습니다.
위 작업을 통해 할일을 삭제하거나 새로 추가해도 새로고침없이 바로 확인이 가능합니다.