Vue(8): TodoList case 1.0

1. Idea analysis

1. Requirements:

(1) Enter text in the input box to add agency options.
(2) Completed options can be checked.
(3) Completed options can be deleted.
(4) All interactions can be selected at the bottom to display the total number of completed events.
(5) All completed tasks can be cleared in the lower right corner.

2. Analyze structure

Determine the component name (Header, List, Item, Footer) and number, as well as the nesting relationship (List contains Iterm, List and Hello\Footer are sibling components), and then introduce the corresponding components.
Split the html and css and put them into corresponding components respectively. Add the scoped attribute to the style tag (to ensure that the current style is only used in the current component to prevent the style name from being reused)

3.Initialization list

Define an array todos in List to store each item as an object, then write in the tag of List and use v-for to traverse each object in the array to generate a structure, and Pass each object to the subcomponent Item through the custom attribute todo.

<MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"></MyItem>

Use the props configuration item to receive it in the Item (implementing parent-child component communication), and then bind todo.done to checked in the input box.

4. (Different) Click the button in the lower right corner to delete all completed tasks

Define a method in App and pass it to Footer:

//5. Delete the completed task, no need to pass parameters, just let it be called directly.
deleteDone() {<!-- -->
    if (confirm('Are you sure you want to clear all completed tasks?'))
        this.todos = this.todos.filter((todo) => {<!-- -->
            return todo.done !== true;
        })
}

Call this method in Footer

<button class="btn btn-danger" @click="clearDone">Clear completed tasks</button>
clearDone() {<!-- -->
            this.deleteDone(); //Click to call the method in the App and delete all true
        }

2. Code

1.App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTo="deleteTo" />
        <MyFooter
          :todos="todos"
          :checkAllTodo="checkAllTodo"
          :deleteDone="deleteDone"
        />
      </div>
    </div>
  </div>
</template>

<script>
/* eslint-disable */
import MyHeader from "./components/MyHeader.vue";
import MyList from "./components/MyList.vue";
import MyFooter from "./components/MyFooter.vue";

export default {<!-- -->
  name: "App",
  components: {<!-- --> MyHeader, MyList, MyFooter },
  data() {<!-- -->
    return {<!-- -->
      todos: [
        {<!-- --> id: "001", title: "Skin Care", done: true },
        {<!-- --> id: "002", title: "Makeup", done: false },
        {<!-- --> id: "003", title: "Eat", done: true },
        {<!-- --> id: "004", title: "sleep", done: true },
      ],
    };
  },
  methods: {<!-- -->
    // 1. Add a todo
    addTodo(todoObj) {<!-- -->
      //Add a value in front of the data
      this.todos.unshift(todoObj);
    },
    // 2. Check or uncheck a todo,
    checkTodo(id) {<!-- -->
      this.todos.forEach((todo) => {<!-- -->
        // function body
        if (todo.id === id) todo.done = !todo.done;
      });
    },
    // 3. Delete a todo
    deleteTo(id) {<!-- -->
      this.todos = this.todos.filter((todo) => todo.id !== id);
    },
    // 4. Select all or cancel all selections
    checkAllTodo(done) {<!-- -->
      this.todos.forEach((todo) => {<!-- -->
        todo.done = done;
      });
    },

    //5. Delete the completed task. No need to pass parameters, just let the other side call it.
    deleteDone() {<!-- -->
      if (confirm("Are you sure you want to clear all completed tasks?"))
        this.todos = this.todos.filter((todo) => {<!-- -->
          return todo.done !== true;
        });
    },
  },
};
</script>

2.MyHeader.vue

<template>
  <div class="todo-header">
    <input
      type="text"
      placeholder="Please enter your task name and press Enter to confirm"
      @keyup.enter="add"
      v-model="title"
    />
  </div>
</template>

<script>
import {<!-- --> nanoid } from "nanoid";
export default {<!-- -->
  name: "MyHeader",
  data() {<!-- -->
    return {<!-- -->
      title: "",
    };
  },
  props: ["addTodo"],
  methods: {<!-- -->
    add() {<!-- -->
      //Verify data
      if (!this.title.trim()) return alert("The input cannot be empty");
      //Wrap user input into a todo object
      // eslint-disable-next-line
      const todoObj = {<!-- --> id: nanoid(), title: this.title, done: false };
      //Notify the App component to add a todo object
      this.addTodo(todoObj);
      // clear input
      this.title = "";
    },
  },
};
</script>

3.MyList.vue

<template>
  <ul class="todo-main">
    <!-- Traversal -->
    <MyItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
      :deleteTo="deleteTo"
    />
  </ul>
</template>

<script>
import MyItem from "./MyItem.vue";
export default {<!-- -->
  name: "MyList",
  components: {<!-- --> MyItem },
  props: ["todos", "checkTodo", "deleteTo"],
};
</script>

4.MyItem.vue

<template>
  <li>
    <label>
      <!-- The first way to write input @click="handleCheck(todo.id)" -->
      <!--The second way of writing @change="handleCheck(todo.id) -->
      <!-- v-model two-way binding can directly affect Item. The following code can also implement the function but is not recommended, but it modifies props -->
      <!-- <input type="checkbox" v-model="todo.done" /> -->
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
      <span>{<!-- -->{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">Delete</button>
  </li>
</template>

<script>
export default {<!-- -->
  name: "MyItem",
  // Declare to receive todo object
  props: ["todo", "checkTodo", "deleteTo"],
  methods: {<!-- -->
    // eslint-disable-next-line
    // Check or uncheck
    handleCheck(id) {<!-- -->
      // Notify the App component to invert the done value of the corresponding todo object (where the data is, the method of operating the data is written there)
      this.checkTodo(id);
    },
    // delete
    // eslint-disable-next-line
    handleDelete(id) {<!-- -->
      // confirm determines whether the Boolean value is true or false based on user interaction
      // eslint-disable-next-line
      if (confirm("Are you sure to delete?")) {<!-- -->
        this.deleteTo(id);
      }
    },
  },
};
</script>

5.MyFooter.vue




3. Summary

1. Component coding process: ?

(1). Split static components: Components should be split according to function points, and the naming should not conflict with html elements. ?
(2). Implement dynamic components: Consider the storage location of the data, whether the data is used by one component or by several components:
A component is in use: just place it on the component itself. ?
Some components use: State promotion on their common parent component.
? (3). Implement interaction: start with binding events.

2.props applies to: ?

(1). Parent component ==> Child component communicates? (2). Child component ==> Parent component communicates (requires the parent to give the child a function first)

3. Remember when using v-model:

The value bound to v-model cannot be the value passed by props, because props cannot be modified!

Note on 4.props:

If the value passed by props is an object type value, Vue will not report an error when modifying the properties in the object, but this is not recommended.