Vuefire CRUD Todo List App — Part 2

Use Vue.js, Firebase’s Cloud Firestore, and Vuefire to create a full-stack app

Nathan Magyar
5 min readMar 17, 2019

This is the second article in a two-part series on creating a simple CRUD (create, read, update, delete) todo list application. In Part 1, we added new todos and updated their completed attributes, but we still need to add functionality for deleting todos and editing a todo’s text.

Step 1: Add deleteTodo() method

Start by copying the following SVG code into a file called trash.svg. Save it in the assets directory.

<svg width="16" height="18" viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg">
<g id="Page-1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-11 -9)" fill="#FF33AE">
<path d="M16.714 16.071V22.5c0 .2-.156.357-.357.357h-.714a.353.353 0 0 1-.357-.357v-6.429c0-.2.156-.357.357-.357h.714c.201 0 .357.157.357.357zm2.857 0V22.5c0 .2-.156.357-.357.357H18.5a.353.353 0 0 1-.357-.357v-6.429c0-.2.156-.357.357-.357h.714c.201 0 .357.157.357.357zm2.858 0V22.5c0 .2-.157.357-.358.357h-.714A.353.353 0 0 1 21 22.5v-6.429c0-.2.156-.357.357-.357h.714c.201 0 .358.157.358.357zm1.428 8.08v-10.58h-10v10.58c0 .536.301.849.357.849H23.5c.056 0 .357-.313.357-.848zm-7.5-12.008h5l-.536-1.306a.43.43 0 0 0-.19-.123h-3.537a.376.376 0 0 0-.19.123l-.547 1.306zm10.357.357v.714c0 .201-.156.357-.357.357h-1.071v10.58c0 1.228-.804 2.278-1.786 2.278h-9.286c-.982 0-1.785-1.005-1.785-2.233V13.571h-1.072a.353.353 0 0 1-.357-.357V12.5c0-.2.156-.357.357-.357h3.449l.781-1.864c.223-.547.893-.993 1.484-.993h3.572c.591 0 1.261.446 1.484.993l.781 1.864h3.45c.2 0 .356.156.356.357z" id="trash"/>
</g>
</g>
</svg>

Then inside App.vue, add a button element for deleting each todo:

<ul class="todo-list">
<li v-for="todo in todos" :key="todo.id" class="todo-item">
<label v-if="currentlyEditing !== todo.id" class="todo-item-label">
<input
type="checkbox"
v-model="todo.completed"
@change="updateTodo(todo)"
class="todo-item__checkbox">
{{todo.text}}
</label>
<button
@click="deleteTodo(todo)"
class="todo-button">
<img src="./assets/trash.svg" alt="Delete todo">
</button>
</li>
</ul>

The new button uses the SVG we just added, and has a click handler of deleteTodo(), which receives the todo we want to delete as an argument. There’s also a todo-button class, for which you can add these styles:

.todo-button {
background: transparent;
border: 0;
padding: .5rem;
width: 40px;
height: 40px;
border-radius: 3px;
cursor: pointer;
}

All we need to do now is write the deleteTodo() method, which is as simple as locating the todo document we want to delete from todosCollection by its id, then using the delete() method:

deleteTodo(todo) {
todosCollection.doc(todo.id).delete();
}

Step 2: Add todo text edit functionality

We will handle todo text editing mostly within our App.vue component, then submit the newly updated todo text to the right place in the database. Let’s start by adding two new data properties to our component’s data() function: currentlyEditing (initialized to null) will store the id of the todo document we’re editing and todoEditText (initialized to an empty string) will store the new text we want to save for that todo.

<script>
...
export default {
...,
data () {
return {
newTodo: '',
todos: [],
currentlyEditing: null,
todoEditText: ''
}
},
....
};
</script>

Next make a new SVG file named pencil.svg and save it in assets to use as the graphic for our edit button:

<svg width="17" height="18" viewBox="0 0 17 18" xmlns="http://www.w3.org/2000/svg">
<g id="Page-1" fill="none" fill-rule="evenodd">
<g id="Artboard-Copy" transform="translate(-10 -9)" fill="#FF33AE">
<path d="M14.051 25l1.016-1.016-2.623-2.622-1.015 1.015v1.194h1.428V25h1.194zm5.837-10.357c0-.145-.1-.246-.245-.246a.261.261 0 0 0-.19.078l-6.049 6.05a.261.261 0 0 0-.078.19c0 .144.1.245.245.245a.261.261 0 0 0 .19-.078l6.05-6.05a.261.261 0 0 0 .077-.19zm-.602-2.143l4.643 4.643-9.286 9.286H10v-4.643l9.286-9.286zm7.623 1.071c0 .38-.157.748-.413 1.005l-1.853 1.853L20 11.786l1.853-1.842a1.4 1.4 0 0 1 1.004-.424c.38 0 .748.156 1.016.424l2.623 2.612c.256.268.413.636.413 1.015z" id="pencil"/>
</g>
</g>
</svg>

Then we need to make a couple template updates:

  • Add an edit button (lines 23–27 below) with the pencil.svg and a click method of editTodo(todo). Group it with the delete button by wrapping it in a div so that it aligns nicely on the right side of list.
  • Only show the todo item label (14–21) and action buttons (22–33) if the given todo’s id is not equal to that in currentlyEditing. Otherwise:
  • Show an edit form (34–45) containing a text input that is v-modeled with todoEditText, and a save button. The save button’s click handler uses the .prevent event modifier again to stop the page from reloading when the form is submitted, and calls a method named updateTodoText().

There’s also quite a bit of CSS to add, so here’s everything for App.vue up to this point:

Finally we have to write two methods: editTodo(todo) and updateTodoText(). The first method starts the editing process and the second one handles the saving of our new text to the database. Inside the methods section of App.vue add:

editTodo(todo) {
this.currentlyEditing = todo.id
this.todoEditText = todo.text
},
updateTodoText() {
todosCollection.doc(this.currentlyEditing).update({
text: this.todoEditText
})
.then(function(docRef) {
console.log("Updated document text with ID: ", todo.id);
})
.catch(function(error) {
console.error("Error updating document text: ", error);
});
this.currentlyEditing = null;
this.todoEditText = '';
}

As you see above, editTodo(todo) take’s the id of the todo we pass in and sets it as the value of currentlyEditing, and it takes the todo's text value and sets that as the value of todoEditText. That way when a user wants to edit a todo, they start by seeing whatever the current text value is.

As for updateTodoText(), we pass the id stored in currentlyEditing to locate the right document, then use the update() method to change a specific part of it. To make a specific change, we pass in an object with the key of whatever we want to change, in this case it’s the text value, and the new value, which is stored in todoEditText. A bit of success/error logging takes place afterward, and then we reset currentlyEditing and todoEditText to their initial values, causing the todo item in the template to revert back to its non-editing state.

And there you have it! A simple but complete CRUD application with Vue.js and Firebase. Thanks for reading! 👋

--

--

Nathan Magyar

User Experience Designer and Front End Developer, University of Michigan Office of Academic Innovation