Building a ToDo App With Vue 3 Composition API

Pieces 🌟 - Dec 12 '22 - - Dev Community

The Composition API was a significant advancement in Vue 3. It consists of a group of APIs that let users use Vue's parts by importing functions rather than expressing options as they would when using the Options API.

In this article, we’ll be building a task tracker with Vue 3 Composition API. Here is the link to the application demo.

The finished ToDo Application.

What is the Composition API?

As an alternative to the Options API, a new way to create Vue apps was introduced in Vue 3. It is a functional-based API that enables developers to create Vue components without using declarative functions by employing imported functions.

Why Use Composition API?

Due to the logical data distribution among numerous options, scaling a large Vue application that uses the Optional API can be highly frenetic and demanding. However, the Composition API corrects this by putting all the data that has been divided into options together, making them more flexible and easier to comprehend.

Composition Hooks

The Composition API comes with many custom hooks, some of which we’ll be using in this article. Before hopping in, let's familiarize ourselves with the hooks and their uses:

  • Ref: The ref hook is used to create a local state.
  • Watch: The watch hook initiates an event as soon as a change in a reactive state is noticed.
  • OnMounted: The onMounted hook is a lifecycle hook that registers a callback immediately after the component is mounted.

Prerequisites

To follow along with this tutorial, you’ll need to first clone the stater file from Github.

Setting Up Work Environments

After cloning the repo from Github, let’s navigate to the cloned project directory and run the command below in our terminal to initialize yarn for the project and also run the development server:

yarn && yarn dev
Enter fullscreen mode Exit fullscreen mode

Creating Todo Functionalities

With our project initialized with yarn and running in the browser, we can start creating some basic functionalities.

Initializing Global Variables

In this section, we’ll be initializing some global variables that we’ll use later in our application. First, let’s replace the entire App.vue with the code block below:

<script setup>
import {ref, onMounted, watch} from "vue";

const todos = ref([]);
const text = ref("");

</script>
Enter fullscreen mode Exit fullscreen mode

In the code block above, we're initializing our global variables. The setup attribute added to the <script> tag gives us access to use the Composition API and its hooks.

Creating Todos

Next, let’s create a function that adds todos to our application. To do this, we’ll copy and add the code block below into the script tag:

<script setup>
import {ref, onMounted, watch} from "vue";

const todos = ref([]);
const text = ref("");

</script>
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re pushing an object into the todos array containing a todo and done property whenever the user creates a todo. We’re also checking for empty inputs.

The unshift method adds the current item to the beginning of the array.

Deleting Todos

Let’s give our users the ability to remove unwanted todos from their list. Next, we’ll add the code below the addTodo function in the <script> tag:

function deleteTodo(todo) {
  todos.value = todos.value.filter((x) => x !== todo);
}
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re filtering through the todos array and returning a new array without the todo passed to it.

Adding Todos to Browsers’ Local Storage

We want to persist our user’s data to the browser’s local storage to avoid losing it on page refresh and reload. To achieve this, we’ll add the code below the deleteTodo into the <script> tag:

watch(
 todos,
  (newTodoValue) => {
    localStorage.setItem("todos", JSON.stringify(newTodoValue));
  },
  { deep: true }
);
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re adding a newTodoValue to the todos object in the browser’s local storage by listening to changes in the todos array. We’re also setting the deep property to true to make our watch function listen further in the array for the slightest change.

Retrieving Todos from Browsers’ Local Storage

Finally, let’s retrieve our todos from the browser's local storage whenever our page is mounted. To achieve this, we’ll copy and paste the function below into the <script> tag:

onMounted(() => {
  todos.value = JSON.parse(localStorage.getItem("todos")) || [];
});
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re getting the todos from the browser's local storage whenever the page is mounted, and returning an empty array if the local storage is empty.

Building User Interface

In the previous section, we did a setup of logic and functionalities for our task tracker. Let’s put all those into our user interface.

Handling Todo Input Event

In this section, we’ll be using the logic created earlier to retrieve and store our user’s input data:

<template>
  <main class="app">
    <section class="greeting">
      <h3 class="title">✍️ToDo Application</h3>
    </section>
    <div class="input-section">
      <section class="create-todo">
        <form @submit.prevent="addTodo">
          <h3>What do you plan on doing🙂?</h3>
          <input
            type="text"
            placeholder="e.g. email your boss"
            v-model="text"
          />
          <input type="submit" value="Add todo" />
        </form>
      </section>
    </div>

  </main>
</template>
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re passing the user’s data from the input text into the addTodo function. The addTodo function adds this data to our todo array in our script tag.

Displaying the Todo List

After adding the todo to our todos array, we should visualize it in our UI. To achieve this, we’ll copy and paste the code below the create-task section:

<template>
  <main class="app">

  ...    <!-- previous code block here -->

    <div class="todo-section">
      <section class="todo-list">
        <h2 v-show="todos.length === 0">No Todos Here😞</h2>
        <div class="list">
          <div
            v-for="todo in todos"
            :class="`todo-item ${todo.done && 'done'}`"
          >
            <label>
              <input type="checkbox" v-model="todo.done" />
            </label>
            <div class="todo-content">
              <input type="text" v-model="todo.todo" />
            </div>
            <div class="actions">
              <button class="delete" @click="deleteTodo(todo)">Delete</button>
            </div>
          </div>
        </div>
      </section>
    </div>

  </main>
</template>
Enter fullscreen mode Exit fullscreen mode

In the code block above, we’re looping through the todos array to display each todo and a checkbox to complete the task. We’re also calling the deleteTodo function to delete a particular todo from the list, and displacing a text if the todos array is empty.

With our progress in this article, we’ve been able to achieve the results below:

The finalized todo Application.

Resources

Here is the link to the full source code on Github.

Conclusion

In this article, we’ve learned how to use the most popular Composition API hooks. We’ve also used the Vue Composition API to conduct some basic operations ranging from creating, retrieving, and deleting data. Due to its hook-based simplicity, the Composition API will always be the best and most significant feature in Vue 3.

Visit the Official Documentation for more information on other Vue's Composition API Hooks.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .