본문 바로가기
Dev/Vue.js

[Vue.js] Todo App 개선하기

by dev_jsk 2020. 9. 10.
728x90
반응형

개선사항

할 일 추가, 삭제나 Clear All 시 리스트에 바로 반영하기

원인

할 일 추가(TodoInput), 리스트(TodoList), Clear All(TodoFooter) 컴포넌트가 나뉘어져 있어 컴포넌트끼리 변경사항을 전달하지 않았기 때문에 localStorage에서는 변경사항이 보이지만 화면에서는 확인되지 않는다.

[리팩토링] 할 일 목록 표시 기능

기능

  • App.vue로부터 props 통신을 사용하여 todoItems를 받아 TodoList에 출력
// App.vue

<template>
  <div id="app">
    <!-- <TodoList v-bind:내려보낼 프롭스 속성 이름="현재 위치의 컴포넌트 데이터 속성"></TodoList> -->
    <TodoList v-bind:propsdata="todoItems"></TodoList>
  </div>
</template>

<script>
import TodoList from './components/TodoList.vue'

export default {
  data: function() {
    return {
      todoItems: []
    }
  },
  created: function() {
    if(localStorage.length > 0) {
      for(var i = 0; i < localStorage.length; i++) {
        if(localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoList': TodoList
  }
}
</script>

<style>
...
</style>
// TodoList.vue

<template>
  <div>
      <ul>
          <!-- 기존 in todoItems 대신 propsdata 사용 -->
          <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)"></i>
              <span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
              <span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
                  <i class="fas fa-trash"></i>
              </span>
          </li>
      </ul>
  </div>
</template>

<script>
export default {
    props: ['propsdata'],
    methods: {
        removeTodo: function(todoItem, index) {
            localStorage.removeItem(todoItem);

            this.todoItems.splice(index, 1);
        },
        toggleComplete: function(todoItem) {
            todoItem.completed = !todoItem.completed;
            localStorage.removeItem(todoItem.item);
            localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
        }
    },
}
</script>

<style scoped>
...
</style>

[리팩토링] 할 일 추가 기능

기능

  • TodoInput에서 event emit 통신을 사용하여 입력 값 전달 및 추가 후 리스트에 바로 반영
// App.vue

<template>
  <div id="app">
    <!-- <TodoInput v-on:하위 컴포넌트에서 발생시킨 이벤트 이름="현재 컴포넌트의 메소드명"></TodoInput> -->
    <TodoInput v-on:addTodoItem="addOneItem"></TodoInput>
    <TodoList v-bind:propsdata="todoItems"></TodoList>
  </div>
</template>

<script>
import TodoInput from './components/TodoInput.vue'
import TodoList from './components/TodoList.vue'

export default {
  data: function() {
    return {
      todoItems: []
    }
  },
  methods: {
    addOneItem: function(todoItem) {
      var obj = {completed: false, item: todoItem};
      localStorage.setItem(todoItem, JSON.stringify(obj));
      this.todoItems.push(obj);
    }
  },
  created: function() {
    if(localStorage.length > 0) {
      for(var i = 0; i < localStorage.length; i++) {
        if(localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoInput': TodoInput,
    'TodoList': TodoList
  }
}
</script>

<style>
...
</style>
// TodoInput.vue

<template>
  <div class="inputBox shadow">
      <input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
      <span class="addContainer" v-on:click="addTodo">
          <i class="fas fa-plus addBtn"></i>
      </span>
  </div>
</template>

<script>
export default {
    data: function() {
        return {
            newTodoItem: ''
        }
    },
    methods: {
        addTodo: function() {
            if(this.newTodoItem !== '') {
                // this.$emit('이벤트 이름', 인자1, 인자2, ...);
                // event emit으로 추가할 item 전달
                this.$emit('addTodoItem', this.newTodoItem);
                this.clearInput();
            }
        },
        clearInput: function() {
            this.newTodoItem = '';
        }
    }
}
</script>

<style scoped>
...
</style>

[리팩토링] 할 일 삭제 기능

기능

  • TodoList에서 event emit 통신을 사용하여 삭제할 값 전달 및 삭제 후 리스트에 바로 반영
// App.vue

<template>
  <div id="app">
    <TodoList v-bind:propsdata="todoItems" v-on:removeItem="removeOneItem"></TodoList>
  </div>
</template>

<script>
import TodoList from './components/TodoList.vue'

export default {
  data: function() {
    return {
      todoItems: []
    }
  },
  methods: {
    removeOneItem: function(todoItem, index) {
      // localStorage.removeItem(key)
      // TodoList에서 todoItem 객체를 받았기 때문에 키 값과 동일한 todoItem.item을 사용해야 한다.
      localStorage.removeItem(todoItem.item);
      this.todoItems.splice(index, 1);
    }
  },
  created: function() {
    if(localStorage.length > 0) {
      for(var i = 0; i < localStorage.length; i++) {
        if(localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoList': TodoList
  }
}
</script>

<style>
...
</style>
// TodoList.vue

<template>
  <div>
      <ul>
          <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)"></i>
              <span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
              <span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
                  <i class="fas fa-trash"></i>
              </span>
          </li>
      </ul>
  </div>
</template>

<script>
export default {
    props: ['propsdata'],
    methods: {
        removeTodo: function(todoItem, index) {
            // event emit으로 삭제할 item과 index 전달
            this.$emit('removeItem', todoItem, index);
        },
        toggleComplete: function(todoItem) {
            todoItem.completed = !todoItem.completed;
            localStorage.removeItem(todoItem.item);
            localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
        }
    },
}
</script>

<style scoped>
...
</style>

[리팩토링] 할 일 완료 기능

기능

  • TodoList에서 event emit 통신을 사용하여 변경할 값 전달 및 변경 후 리스트에 바로 반영
// App.vue

<template>
  <div id="app">
    <TodoList v-bind:propsdata="todoItems" v-on:toggleItem="toggleOneItem"></TodoList>
  </div>
</template>

<script>
import TodoList from './components/TodoList.vue'

export default {
  data: function() {
    return {
      todoItems: []
    }
  },
  methods: {
    toggleOneItem: function(todoItem, index) {
      // 하위로 내려준 데이터를 다시 받아 직접 접근하는 방식은 좋지 못함
      // todoItem.completed = !todoItem.completed;
      this.todoItems[index].completed = !this.todoItems[index].completed;
      localStorage.removeItem(todoItem.item);
      localStorage.setItem(todoItem.item, JSON.stringify(todoItem));
    }
  },
  created: function() {
    if(localStorage.length > 0) {
      for(var i = 0; i < localStorage.length; i++) {
        if(localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoList': TodoList
  }
}
</script>

<style>
...
</style>
// TodoList.vue

<template>
  <div>
      <ul>
          <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>
              <span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
              <span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
                  <i class="fas fa-trash"></i>
              </span>
          </li>
      </ul>
  </div>
</template>

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

<style scoped>
...
</style>

[리팩토링] 할 일 모두 삭제 기능

기능

TodoFooter에서 event emit 통신을 사용하여 전체 삭지 및 리스트에 바로 반영

// App.vue

<template>
  <div id="app">
    <TodoFooter v-on:clearAll="clearAllItems"></TodoFooter>
  </div>
</template>

<script>
import TodoFooter from './components/TodoFooter.vue'

export default {
  data: function() {
    return {
      todoItems: []
    }
  },
  methods: {
    clearAllItems: function() {
      // localStorage 처리 부분
      localStorage.clear();
      // 화면 스크립트 처리 부분
      this.todoItems = [];
    }
  },
  created: function() {
    if(localStorage.length > 0) {
      for(var i = 0; i < localStorage.length; i++) {
        if(localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
        }
      }
    }
  },
  components: {
    'TodoFooter': TodoFooter
  }
}
</script>

<style>
...
</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>
...
</style>
728x90
반응형

'Dev > Vue.js' 카테고리의 다른 글

[Vue.js] ES6  (0) 2020.09.11
[Vue.js] Todo App 사용자 경험 개선하기  (0) 2020.09.10
[Vue.js] Todo App 만들기  (0) 2020.09.10
[Vue.js] 싱글 파일 컴포넌트  (0) 2020.09.09
[Vue.js] Vue CLI  (0) 2020.09.09

댓글