본문 바로가기
💻CODING/react. vue

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

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

 

vue.js와 vue material을 이용한
todolist 앱 리팩토링

 

리팩토링 전의 앱의 구현 내용은 아래의 포스트로 이동해주세요.

https://goddino.tistory.com/90

 

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

vue.js와 vue material을 이용한 간단한 todolist 앱을 만들겠습니다. (before refactoring) 예제는 인프론의 Vue.js 끝장내기 강의를 듣고 참조하여 공부한 내용입니다. Todolist 앱화면 구현 내용: · header,..

goddino.tistory.com

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

 

TodoList 앱화면

 

리팩토링 작업 과정:

하위 컴포넌트의 훅, 데이터, 메소드를 상위 컴포넌트로 옮긴다.

TodoList.vue 파일(할일 목록 표시)
1. TodoList.vue에서 create lifecycle hook과 data todos를 App.vue로 올린다.
2. App.vue에서 TodoList.vue로, 데이터 todos를 props를 통해 넘겨준다.

TodoInput.vue 파일(할일 추가 표시)
3. TodoInput.vue에서 클릭이벤트 add 메소드를 App.vue로 emit.event를 통해 올린다.
4. 위 메소드로 추가된 obj를 todos 배열에 추가하여 바로 list에 자동생성 한다.

TodoList.vue 파일(할일 삭제)
5. TodoList.vue에서 클릭이벤트 remove 메소드를 App.vue로 emit.event를 통해 올린다.

TodoList.vue 파일(할일 완료)
6. TodoList.vue에서 클릭이벤트 toggle 메소드를 App.vue로 emit.event를 통해 올린다.

TodoFooter.vue 파일(할일 모두 삭제)
7. TodoFooter.vue에서 클릭이벤트 clear 메소드를 App.vue로 emit.event를 통해 올린다.

 

App.vue

<template>
    <div id="app">
        <TodoHeader></TodoHeader>
        <TodoInput v-on:addOne="addFn"></TodoInput>
        <TodoList v-bind:propsdata="todos" 
        v-on:removeOne="removeFn" 
        v-on:toggleOne="toggleFn"></TodoList>
        <TodoFooter v-on:clearOne="clearFn"></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: function (){
            // TodoList에서 가져옴 -> TodoList에 props로 내림
            return { todos : [] }
        },
        methods: {
            // TodoInput에서 $emit.event 가져옴
            addFn(todoItem){
                //var obj = { completed: false, item: this.doItem };
                var obj = { completed: false, item: todoItem };
                //localStorage.setItem(this.doItem, JSON.stringify(obj));
                localStorage.setItem(todoItem, JSON.stringify(obj));
                //obj를 todos에 추가 (더이상 새로고침하지 않아서 아래 리스트에 자동생성)
                //todos에서 list와 input에서 가져온 것을 todos 하나로 관리
                this.todos.push(obj);
            },
            // TodoList에서 $emit.event 가져옴
             removeFn(todo, index){
                //localStorage.removeItem(todo); todo에 객체 전체가 들어오므로,
                //화면에서는 삭제가 되어도, localstorage에서는 안없어짐
                //todo.item으로 key 값을 정확하게 지울 수 있음
                localStorage.removeItem(todo.item); 
                this.todos.splice(index, 1);
            },
             // TodoList에서 $emit.event 가져옴
            toggleFn(todo, index){ 
                //todo.completed = !todo.completed;
                this.todos[index].completed = !this.todos[index].completed
                //localstorage 업데이트
                localStorage.removeItem(todo.item);
                localStorage.setItem(todo.item, JSON.stringify(todo));
            },
            clearFn(){
                localStorage.clear();
                console.log(this.todos);
                this.todos = []; //this.todos = '' or null 아님

            }
        },
        // TodoList에서 가져옴
        created() {
            if (localStorage.length > 0) {
                for (let i = 0; i < localStorage.length; i++) {
                    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))));
                    }
                }
            }
        },
        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>
                    <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) { // this.$emit('이벤트이름', 인자1, 인자2);
                    this.$emit('addOne', this.doItem); 
                    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>
          
            <li v-for="(todo, index) in propsdata" v-bind:key="todo">
                <div>
                    <i class="fas fa-check checkBtn"
                        @click="toggleFn(todo, index)"
                        v-bind:class="{checkBtnCompleted: todo.completed}"></i>
                    <span @click="toggleFn(todo, index)" 
                    :class="{textCompleted: todo.completed}">{{todo.item}}</span>
                </div>
                <span class="removeBtn" @click="removeFn(todo, index)">
                    <i class="far fa-trash-alt"></i>
                </span>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        props: ['propsdata'], //todos를 App.vue에 옮겨가고, props로 받는다. todos -> propsdata
        methods: {
            removeFn(todo, index) {
                this.$emit('removeOne', todo, index );
            },
            toggleFn(todo, index) {
                this.$emit('toggleOne', todo, index);
            }
        }
    }
</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() {
                this.$emit('clearOne');
            }
        }
    }
</script>

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



 



반응형

댓글