DEV Community

Cover image for Learning Vue Part 2: Building a to-do app
William Kwadwo Owusu
William Kwadwo Owusu

Posted on

Learning Vue Part 2: Building a to-do app

After completing my first project—a weather app—I decided to take on a second project to further explore Vue.js: a to-do list app. This project allowed me to dive deeper into Vue’s reactivity system, local storage, and form handling. Here’s a breakdown of what I’ve learned from building it.

1. Form Handling: Preventing Page Refresh with @submit.prevent

One of the first things I realized was how powerful Vue’s form handling is, especially the @submit.prevent directive. By using this, I was able to prevent the default behavior of form submissions, which typically results in a page refresh. In my app, I used @submit.prevent="addTask" to ensure that when users add a task, the page doesn’t reload.

<form @submit.prevent="addTask" class="d-flex justify-content-center">
    <div class="form-group">
        <input type="text" class="form-control" placeholder="Add task..." v-model="task">
    </div>
    <span class="ml-2"><button type="submit" class="btn btn-success">Add</button></span>
</form>
Enter fullscreen mode Exit fullscreen mode

However, I learned that when you prevent form submission in Vue, you need to ensure the input field has a v-model bound to a reactive data property. Otherwise, the form may still behave as if it’s refreshing since the input value isn't being tracked or updated in Vue’s reactive system.

2. Reactivity: Managing Tasks with ref

Just like in my weather app, I used Vue’s ref function to manage the state of my tasks and the current input. This allowed me to store tasks in an array and dynamically update the UI when new tasks were added or deleted:

const task = ref('');
const tasks = ref([]);

const addTask = () => {
    if (task.value.trim() === '') return; // prevent adding empty tasks
    let taskObject = { id: Date.now(), name: task.value, checked: false };
    tasks.value.push(taskObject);
    updateLocalStorage();
    task.value = '';
};
Enter fullscreen mode Exit fullscreen mode

This reactivity is key to keeping the app dynamic. Any changes to the tasks array automatically trigger re-renders in the UI, without any need for manual DOM manipulation. This is where Vue really shines.

3. Local Storage: Making the App Persistent

One of the key features I wanted to implement in my to-do list app was persistence. I didn’t want users to lose their tasks when they refreshed the page, so I decided to store the tasks in localStorage. I learned that Vue’s onMounted lifecycle hook is perfect for restoring data when the app loads:

onMounted(() => {
    const storedTasks = localStorage.getItem('tasks');
    tasks.value = storedTasks ? JSON.parse(storedTasks) : [];
});
Enter fullscreen mode Exit fullscreen mode

This way, when the user opens the app, it checks localStorage for any previously saved tasks and loads them into the app’s state. Whenever a task is added, modified, or deleted, the updateLocalStorage function is called to save the latest state of the tasks:

const updateLocalStorage = () => {
    localStorage.setItem('tasks', JSON.stringify(tasks.value));
};
Enter fullscreen mode Exit fullscreen mode

This was a valuable lesson in making apps more user-friendly by maintaining state across sessions.

4. Task Management: Checkboxes and Deletion

Managing the completion status of tasks was another fun challenge. By using Vue’s v-model to bind the checkbox to each task’s checked property, I was able to make each task reactive to user input:

<input type="checkbox" class="form-check-input" :id="index" v-model="todo.checked" @change="changeTaskStatus($event)">
Enter fullscreen mode Exit fullscreen mode

This allowed me to visually update the task’s status with a strikethrough when it was checked:

<label class="form-check-label" :style="{'text-decoration': todo.checked ? 'line-through' : 'none'}">{{todo.name}}</label>
Enter fullscreen mode Exit fullscreen mode

For task deletion, I wanted to make sure users didn’t accidentally delete tasks, so I integrated SweetAlert2 to confirm the deletion:

const deleteTask = (taskId) => {
    Swal.fire({
        title: "Are you sure you want to delete the task?",
        showDenyButton: true,
        confirmButtonText: "Yes, delete",
        denyButtonText: `No`
    }).then((result) => {
        if (result.isConfirmed) {
            tasks.value.splice(taskId, 1);
            updateLocalStorage();
            Swal.fire("Deleted!", "", "success");
        } else {
            Swal.fire("Task not deleted", "", "info");
        }
    });
};
Enter fullscreen mode Exit fullscreen mode

This was a good opportunity to explore how Vue can easily interact with external libraries like SweetAlert2 for enhanced user experience.

5. Leveraging v-for for Task Iteration

Displaying multiple tasks was another instance where Vue’s v-for directive came in handy. This allowed me to loop through the tasks array and render each task dynamically:

<div v-for="(todo, index) in tasks" :key="index">
    <div class="form-group form-check d-flex align-items-center">
        <input type="checkbox" class="form-check-input" :value=todo.name :id="index" v-model="todo.checked" @change="changeTaskStatus($event)">
        <label class="form-check-label" :style="{'text-decoration': todo.checked ? 'line-through' : none}">{{todo.name}}</label>
        <span class="ml-auto text-danger" @click="deleteTask(index)">🗑️</span>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

This directive was key to ensuring that each task was properly rendered, updated, and managed in a way that was reactive to user interaction.

Final Thoughts: What I’ve Gained from This Project

This to-do list project deepened my understanding of Vue’s reactivity system, especially when working with forms and local storage. It’s amazing how Vue makes tasks like managing form submissions, updating the UI based on user interactions, and persisting data so much easier than if I were to do it manually.

In particular, learning how to:

  • Prevent form submissions while ensuring reactive data binding with v-model
  • Manage tasks and local storage for persistence
  • Use external libraries like SweetAlert2 for improved UX

...has given me confidence in Vue’s capabilities. If you’re new to Vue or just looking for a fun project, a to-do list is a great way to get more comfortable with core concepts like reactivity, form handling, and lifecycle hooks.

Happy coding!

Top comments (0)