์บกํดํ๊ต๋์ Vue.js ์ค๊ธ ๊ฐ์ข - ์น์ฑ ์ ์์ผ๋ก ๋ฐฐ์๋ณด๋ Vue.js, ES6, Vuex ๊ฐ์๋ฅผ ๋ฐํ์ผ๋ก ์ค์ตํ ๋ด์ฉ์ ๋๋ค.
TodoHeader.vue
<template>
<header>
<h1>TODO it!</h1>
</header>
</template>
<!-- ์ปดํฌ๋ํธ ์์์๋ง ์ ํจํ ์คํ์ผ ์์ฑ์ ์ง์ ํ๋ ค๋ฉด scoped ์ฒ๋ฆฌ -->
<style scoped>
h1 {
color: #2F3B52;
font-weight: 900;
margin: 2.5rem 0 1.5rem;
}
</style>
TodoInput.vue
<template>
<div class="inputBox shadow">
<input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
<!-- <button v-on:click="addTodo">add</button> -->
<span class="addContainer" v-on:click="addTodo">
<i class="fa-solid fa-plus addBtn"></i>
</span>
</div>
</template>
<script>
export default {
data : function() {
return {
//์ด๊ธฐ ์ ์ธ
newTodoItem : ""
}
},
methods : {
addTodo : function() {
//inputํ๊ทธ์ ์ด๋ค ๊ฐ์ด ์์๋๋ง ๊ฐ์ ์ถ๊ฐํด์ค
if(this.newTodoItem != '')
{
//emit ์ด๋ฒคํธ ์ด๋ฆ ์ง์ , ๋ณด๋ด์ค ์ธ์ 1,2,3,4,...
this.$emit('addTodoItem', this.newTodoItem);
this.clearInput();
}
},
clearInput : function() {
this.newTodoItem = '';
}
}
}
</script>
<style scoped>
input:focus {
outline: 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: linear-gradient(to right, #6478FB, #8763FB);
display: block;
width: 3rem;
border-radius: 0 5px 5px 0;
}
.addBtn {
color: white;
vertical-align: middle;
}
</style>
TodoList.vue
<template>
<div>
<!-- ul>li*3-->
<ul>
<!--
<li v-for="todoItem in todoItems" v-bind:key="todoItem" class="shadow">
-->
<li v-for="(todoItem, index) in propsdata" 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>
<!-- todoitem์ ๋ฐฐ์ด์ ๋ด๊ธด ๊ฐ์ฒด์ ์์ฑ ์ค completed ์์ฑ์ด true๋ฉด ํด๋น ํ๊ทธ๊ฐ ๊ทธ๋๋ก ์ ์ฉ. false๋ฉด {} ์ฒ๋ฆฌ๋์ด์ง -->
<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 {
//App.vue์์ ๋ด๋ฆฐ propsdata๋ฅผ ๋ฐ๋๋ค.
props : ['propsdata'],
methods : {
removeTodo : function(todoItem, index) {
this.$emit('removeItem', todoItem, index);
},
toggleComplete : function(todoItem, index) {
this.$emit('toggleItem', todoItem, index);
}
}
}
</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: white;
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.vue
<template>
<div class="clearAllContainer">
<span class="clearAllBtn" v-on:click="clearTodo">Clear All</span>
</div>
</template>
<script>
export default {
methods : {
clearTodo : function() {
this.$emit('clearAll');
}
}
}
</script>
<style scoped>
.clearAllContainer {
width: 8.5rem;
height: 50px;
line-height: 50px;
background-color: white;
border-radius: 5px;
margin: 0 auto;
}
.clearAllBtn {
color: #e20303;
display: block;
}
</style>
App.vue
<template>
<div id="app">
<TodoHeader></TodoHeader>
<!-- todoinput์์ ๋ณด๋ธ event๋ฅผ ๋ฐ๊ณ , ๊ทธ๊ฑธ ๋ฐ์ผ๋ฉด์ ์คํํ ๋ฉ์๋๋ addOneItem-->
<TodoInput v-on:addTodoItem="addOneItem"></TodoInput>
<!--๋ด๋ ค๋ณด๋ผ ์ปดํฌ๋ํธ์ ๋ถ์ฌํ ์ด๋ฆ(๋ด๊ฐ ์ง์ ) : ํ์ฌ ์ปดํฌ๋ํธ์์ ๋ถ์ฌ์ง ๋ฐ์ดํฐ ์์ฑ ์ด๋ฆ(์ฌ๊ธฐ์ ๋ฐฐ์ด ์ด๋ฆ)-->
<!--์๋ฌต์ ์ผ๋ก ์ธ์๊น์ง ๊ฐ์ด ๋์ด์ด-->
<TodoList v-bind:propsdata="todoItems"
v-on:removeItem="removeOneItem"
v-on:toggleItem="toggleOneItem">
</TodoList>
<TodoFooter v-on:clearAll="clearAllItems"></TodoFooter>
<my_cmp></my_cmp>
</div>
</template>
<script>
import Vue from "vue"
import TodoHeader from './components/TodoHeader.vue'
import TodoInput from './components/TodoInput.vue'
import TodoList from './components/TodoList.vue'
import TodoFooter from './components/TodoFooter.vue'
var my_cmp = {
template : '<div>made by euna</div>'
}
new Vue(
{
el:'#app',
components : {
'my-cmp': my_cmp
}
}
)
export default {
data : function() {
return {
todoItems : []
}
},
methods : {
//์๋์์ ๋ณด๋ธ ์ธ์ this.$emit('addTodoItem', this.newTodoItem);์ this.newTodoItem
addOneItem : function(todoItem) {
var obj = {complete: false, item: todoItem}; //text๊ฐ๊ณผ boolean๊ฐ์ ์ ์ฅํ ๊ฐ์ฒด ์์ฑ
localStorage.setItem(todoItem, JSON.stringify(obj));
this.todoItems.push(obj);
},
removeOneItem : function(todoItem, index) {
localStorage.removeItem(todoItem.item);
this.todoItems.splice(index, 1);
},
toggleOneItem : function(todoItem, index) {
this.todoItems[index].completed = !this.todoItems[index].completed;
localStorage.removeItem(todoItem.item);
localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
},
clearAllItems : function() {
localStorage.clear();
this.todoItems = [];
}
},
//created:์ธ์คํด์ค๊ฐ ์์ฑ๋์๋ง์ ์คํ๋๋ lifecycle
created : function() {
//console.log('created');
if(localStorage.length > 0) {
for (var i=0; i<localStorage.length; i++) {
if(localStorage.key(i) !== 'loglevel:webpack-dev-server')
{
//localStorage.getItem(localStorage.key(i)); //todoinput์์ stringifyํ ๊ฐ์ด๋ฏ๋ก string type
console.log(JSON.parse(localStorage.getItem(localStorage.key(i))));//stringํ ํ ๊ฒ์ ๋ค์ json parsingํ์ฌ obj๋ก ๋๋ฆผ
this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
//์์์ ์์ฑํ ๋ฐฐ์ด์ ๋ฐ์ดํฐ๋ฅผ push
//this.todoItems.push();
}
}
}
},
components : {
'TodoHeader':TodoHeader,
'TodoInput':TodoInput,
'TodoList':TodoList,
'TodoFooter':TodoFooter,
'my_cmp':my_cmp
}
}
</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>