14. Todo - Update (Text)

간단한 Todo App 을 만들어봅니다.

목표

선택된 Todo 의 텍스트를 업데이트합니다.

실습

앞서했던 상태 업데이트와 비슷합니다. li 에 editing 이라는 class 가 추가되면 해당 Todo 에 숨겨져있던 input 이 나옵니다. 우리는 Todo 가 더블클릭 되었을때 이처럼 변하길 원합니다.

먼저 App.vue 에 변화되는 text 와 id 를 받아 텍스트를 업데이트하는 함수를 만들어보겠습니다.

// App.vue 

export default {
  // ... 
  methods: {
    insertTodo(text) { // ... },
    removeTodo(id) { // ... },
    updateDone(id) { // ... },
    updateTodo({ id, text }) {
      const todos = [...this.todos];
      const todo = todos.find(todo => todo.id === id);

      if (todo) {
        todo.text = text;
        this.todos = todos;
      }
    }
  }
};

id 와 text 를 받아 해당 todo 를 찾아 text 를 업데이트합니다. 마찬가지로 Todo.vue 에서 사용될 함수이기 때문에 아래와 같이 이벤트를 등록해줍니다.

// App.vue 

<template>
  <div id="app">
    <section class="todoapp">
      <Header @insertTodo="insertTodo" />
      <Todo
        :todos="todos"
        @removeTodo="removeTodo"
        @updateDone="updateDone"
        @updateTodo="updateTodo"
      />
      <Footer />
    </section>
  </div>
</template>

이제 Todo.vue 로 넘어가겠습니다. Update 에 대한 모든 정보는 Todo.vue 에서 관리되어집니다. 다른 컴포넌트에서는 Edit 상태에 대한 정보가 전혀 궁금하지 않기 때문입니다.

먼저 Todo.vue 에 edit 데이터를 관리하기 위해 edit state 를 추가합니다. 수정하고 싶은 Todo 가 선택된다면 이 edit state 에 선택된 todo 의 id 와 text 가 채워집니다. text 를 따로 관리하는 이유는 기존 데이터와 독립되게 만들기 위해서입니다. 수정 취소시 원본 데이터는 손상되면 안되기 때문입니다.

// Todo.vue

<script>
export default {
  props: {
    todos: { type: Array, default: () => [] }
  },
  data() {
    return {
      edit: { // edit 데이터를 관리합니다.
        text: "",
        id: -1
      }
    };
  },
  methods: {
   // ...
  }
};
</script>

이제 더블 클릭 했을때 해당 todo 로 edit 데이터를 채워보겠습니다.

// Todo.vue 

<script>
export default {
  props: {
    todos: { type: Array, default: () => [] }
  },
  data() { // ... },
  methods: {
    handleRemove(id) { // ... },
    handleDone(id) { // ... },
    handleEdit({ text, id }) {
      this.edit = {
        text,
        id
      };
    }
  }
};
</script>

handleEdit 는 더블 클릭시 선택된 Todo 의 정보를 받아 edit state 를 채우는 함수입니다.

// Todo.vue 

<template>
  <section class="main">
    <ul class="todo-list">
      <li
        :class="{todo: true, completed: isDone, editing: edit.id === id }"
        v-for="({ id, text, isDone }) in todos"
        :key="id"
      >
        <div class="view">
          <input class="toggle" type="checkbox" :checked="isDone" @click="handleDone(id)" />
          <label @dblclick="handleEdit({ text, id })">{{ text }}</label>
          <button class="destroy" @click="handleRemove(id)"></button>
        </div>
        <input class="edit" type="text" v-model="edit.text" />
      </li>
    </ul>
  </section>
</template>

li 에 editing 이라는 class 조건이 추가되었습니다. edit state 에 있는 id 와 해당 todo 의 id 가 같을때만 input 으로 변경됩니다. label 이 더블클릭 되었을때 해당 handleEdit 함수를 이용하여 edit state 를 업데이트합니다. input 은 v-model 을 이용하여 edit state 의 text 를 바라봅니다. 여기까지하셨다면 아래와 같이 더블클릭 했을때 input 으로 변경되어지고 선택된 todo 의 내용으로 input 이 채워지게됩니다.

이제 input 에 수정될 값을 입력하고 엔터를 쳤을때 해당 Todo 의 내용이 변경되는 처리만 해주면 됩니다. input 에 keypress 이벤트를 걸어주고 엔터가 입력될 경우를 캐치하여 edit state 를 App.vue 에서 등록해둔 이벤트를 실행시켜주면 됩니다.

// Todo.vue

<template>
  <section class="main">
    <ul class="todo-list">
      <li
        :class="{todo: true, completed: isDone, editing: edit.id === id }"
        v-for="({ id, text, isDone }) in todos"
        :key="id"
      >
        <div class="view">
          <input class="toggle" type="checkbox" :checked="isDone" @click="handleDone(id)" />
          <label @dblclick="handleEdit({ text, id })">{{ text }}</label>
          <button class="destroy" @click="handleRemove(id)"></button>
        </div>
        <input class="edit" type="text" v-model="edit.text" @keypress="handleUpdate" />
      </li>
    </ul>
  </section>
</template>

<script>
export default {
  props: {
    todos: { type: Array, default: () => [] }
  },
  data() { // ... },
  methods: {
    handleRemove(id) { // ... },
    handleDone(id) { // ... },
    handleEdit({ text, id }) { // ... },
    handleUpdate({ keyCode }) {
      if (keyCode === 13) {
        this.$emit("updateTodo", this.edit);
        this.edit = { // 추가된 후 edit state 를 리셋합니다
          text: "",
          id: -1
        };
      }
    }
  }
}
</script>

아래와 같이 수정되는 것을 확인 할 수 있습니다.

Last updated