Vuefire CRUD Todo List App — Part 2
Use Vue.js, Firebase’s Cloud Firestore, and Vuefire to create a full-stack app
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 thepencil.svg
and a click method ofeditTodo(todo)
. Group it with thedelete button
by wrapping it in adiv
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’sid
is not equal to that incurrentlyEditing
. Otherwise: - Show an edit form (34–45) containing a
text input
that isv-model
ed withtodoEditText
, 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 namedupdateTodoText()
.
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! 👋