본문 바로가기
개발/React

[vue] vue.js로 todolist 투두리스트앱 만들기(리팩토링 전)

by 코딩하는 갓디노 2020. 11. 20.

 

vue.js와 vue material을 이용한
간단한 todolist 앱을 만들겠습니다.
(before refactoring)

 

예제는 인프론의 Vue.js 끝장내기 강의를 듣고 참조하여 공부한 내용입니다.

Todolist 앱화면

 

구현 내용:

· header, input, list, footer component로 분리
· Things To Do라는 input 박스에 해야할일을 기입
· 해야할일 기입 후 enter 또는 오른쪽 plus버튼 클릭시, 아래 리스트에 추가
· CLEAR ALL 버튼 클릭시 전체 삭제
· refactoring 전 구현이 미완성된 상태
· vue material을 이용한 css
· localstorage를 이용한 데이터 저장/불러오기/삭제하기 기능

 

App.vue

<template>
    <div id="app">
        <TodoHeader></TodoHeader>
        <TodoInput></TodoInput>
        <TodoList></TodoList>
        <TodoFooter></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 {
        components: {
            TodoHeader,
            TodoInput,
            TodoList,
            TodoFooter
        }
    }
</script>

<style>
    body {
        text-align: center;
        background-color: #393D49;
        max-width: 800px;
        width: 100%;
    }
    .md-layout-item {
        padding: 0 5px;
    }
</style>

 

TodoHeader.vue

<template>
    <div class="header">
        <span class="md-headline">Todolist</span>
    </div>
</template>

<script>
    export default {}
</script>

<style>
    .header {
        padding: 30px 0 20px;
    }
</style>

 

TodoInput.vue

<template>
    <div>
        <div class="md-layout" style="margin: 0.5rem; color: #fff !important;">
            <div class="md-layout-item md-size-90">
                <md-field>
                    <label>Things To Do</label>
                    <!-- v-keyup:enter 아님 -->
                    <md-input v-model="doItem" @keyup.enter="addTodo"></md-input>
                </md-field>
            </div>

            <div class="md-layout-item md-size-10" @click="addTodo">
                <i class="fas fa-plus-circle addBtn"></i>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data: function () {
            return {doItem: ''}

        },
        methods: {
            addTodo() {
                if (this.doItem) { //값이 있으면
                    // console.log(this.todoItem); 저장하는 로직(key, value) 객체 생성 
                    //-> check버튼 기능 구현 위하여
                    var obj = {
                        completed: false,
                        item: this.doItem
                    };
                    // JOSN.stringify 자바스크립트 객체를 string으로 변환 JSON.stringify()는 
                    // javascript 객체를 string값으로 변환시킴 
                    // 그 이유: localstorage는 객체 출력이 안되고, string text 값만 출력됨
                    // localStorage.setItem(this.todoItems, this.todoItems)으로 했다가
                    // todoList에서 checkbox의 toggle 메소드 사용을 위해 아래로 변경
                    // obj라는 객체에 담아주고, 아래 localstorage value값을 
                    // string으로 변환하여 입력
                    localStorage.setItem(this.doItem, JSON.stringify(obj));
                    this.clearInput();
                }
            },
            clearInput() {
                this.doItem = '';
            }
        }
    }
</script>

<style>
    .md-field,
    .md-focused,
    .md-input,
    .md-textarea,
    label {
        background: #365FD9 !important;
        border-style: none;
        border-radius: 5px;
        margin: 0 0 5px 0 !important;
        color: #fff !important;
        -webkit-text-fill-color: #ddd !important;
    }
    .addBtn {
        vertical-align: middle;
        margin-top: 12px;
        font-size: 24px;
        cursor: pointer;
    }
</style>





Todolist.vue

<template>
    <div>
        <ul>
            <!-- 중요 v-for instance특징, 반복문구의 index를 알려준다 -->
            <!-- 클릭 remove event 위하여 todoItem, index(삭제할 해당건)를 메소드에 넘겨준다 -->
            <li v-for="(todo, index) in todos" v-bind:key="todo">
                <div>
                    <!-- todo.completed값이 true면 checkBtnCompleted class가 동작한다 -->
                    <i
                        class="fas fa-check checkBtn"
                        @click="toggleFn(todo, index)"
                        v-bind:class="{checkBtnCompleted: todo.completed}"></i>
                    <!-- 중요 v-bind 클래스 기존의 html 속성중 todo.completed값이 true면 
                    textCompleted class가 동작한다 class on/off-->
                    <span @click="toggleFn(todo, index)" 
                    :class="{textCompleted: todo.completed}">{{todo.item}}</span>
                </div>
                <!-- {{todo}}으로 출력시 { "completed": false, "item": "abcde.." } 로 나옴 
                -> .item 속성접근 -->
                <!-- todo, index를 인자로 넣어 넘긴다 -->
                <span class="removeBtn" @click="removeFn(todo, index)">
                    <i class="far fa-trash-alt"></i>
                </span>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        data: function () {
            return {
                //localstorage에서 가져올 담는 공간
                todos: []
            }
        },
        //lifecycle 중 instance가 생성되자마다 호출되는 lifecycle hook
        created() {
            if (localStorage.length > 0) {
                for (let i = 0; i < localStorage.length; i++) {
                    // localstorage key값을 getItem하면 value가 output됨 value가
                    // json.stringify(input.vue파일)에서 string type 상태 
                    //string에서 객체형태로 변환해줘야함 json.parse();
                    if (localStorage.key(i) !== 'loglevel:webpack-dev-server') {
                        console.log(localStorage.getItem(localStorage.key(i)));
                        this.todos.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
                    }
                }
            }
        },
        methods: {
            removeFn(todo, index) {
                //console.log(todo, index);
                //localStorage.removeItem(todo); todo에 객체 전체가 들어오므로,
                //화면에서는 삭제가 되어도, localstorage에서는 안없어짐
                //todo.item으로 key 값을 정확하게 지울 수 있음
                localStorage.removeItem(todo.item); 
                this.todos.splice(index, 1);
            },
            toggleFn(todo) {
                todo.completed = !todo.completed;
                //localstorage 업데이트
                localStorage.removeItem(todo.item);
                localStorage.setItem(todo.item, JSON.stringify(todo));
            }
        }
    }
</script>

<style>
    ul {
        list-style-type: none;
        padding-left: 0;
        margin-top: 0;
        text-align: left;
    }
    li {
        display: flex;
        min-height: 50px;
        height: 50px;
        line-height: 50px;
        margin: 1rem;
        padding: 0 0.9rem;
        background: #4872F0;
        border-radius: 5px;
        justify-content: space-between;
    }
    span {
        cursor: pointer;
        color: #fff !important;
    }
    .checkBtn {
        line-height: 45px;
        color: #F0C148;
        margin-right: 20px !important;
        cursor: pointer;
    }
    .checkBtnCompleted {
        text-decoration: line-through;
    }
    .textCompleted {
        text-decoration: line-through;
    }
    .removeBtn {
        cursor: pointer;
    }
</style>

 

TodoFooter.vue

<template>
    <div class="clearAllList">
        <md-button class="clearAllBtn" @click="clearAll">clear all</md-button>
    </div>
</template>

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

<style>
    .clearAllBtn {
        background-color: #F2B705 !important;
    }
</style>





반응형

댓글