Vue.js Basics Part 3 | Computed Property
Ahmet Meliksah Akdeniz
Posted on September 1, 2022
Today, I will be talking about computed property in Vue.js. First, I will code this tutorial from vuejs.org. Afterwards, I will touch the differences between methods
and computed
properties in Vue.js (not in detail).
This is part 3 of my Vue.js Basics series, to read part 2, please go to here
Here's the whole code:
<script>
let id = 0
export default {
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
computed: {
hideCompletedTodos() {
return this.hideCompleted ? this.todos.filter(item => !item.done) : this.todos
}
},
methods: {
addTodo() {
this.todos.push({ id: id++, text: this.newTodo, done: false })
this.newTodo = ''
},
removeTodo(todo) {
this.todos = this.todos.filter(item => item.id !== todo.id)
},
toggleHideCompleted() {
this.hideCompleted = !this.hideCompleted
}
}
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" />
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in hideCompletedTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="toggleHideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>
<style>
.done {
text-decoration: line-through;
}
</style>
Time to read to read code above in small chunks!
export default {
data() {
return {
newTodo: '',
hideCompleted: false,
todos: [
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
]
}
},
In our data(){}
property we've got newTodo
for text
key property in our todos
array. hideCompleted
key for hiding completed todos, this is a boolean value (true or false). Its task is to hide completed todos if it's true, and show all todos regardless of they're completed or not whhen it's false. Finally, we've got an array of objects that stores our todos. As you notice, we've got a new key (done:
) in our objects. That's to alter styling for tasks those are done, later on that one.
Next, computed property:
computed: {
hideCompletedTodos() {
return this.hideCompleted ? this.todos.filter(item => !item.done) : this.todos
}
},
hideCompletedTodos()
function returns something based on hideCompleted
's boolean value (true or false). If hideCompleted
is true
, it filters item
s those item.done
properties are false. In other words, we will have only item.done
with falsy values left because item => !item.done
this code returns items those item.done
values are falsy. : this.todos
this code says, else (if hideCompleted is not true
), then return todos
array altogether.
A bit more about computed. At first glance, computed may look like as if it's the same with methods, but it's not. First of all, computed
re-renders only the changed element, on the other hand, methods
re-render everything. This becomes a performance issue. To solve this use computed
, a computed
property tracks reactive state as dependencies, and caches the result. Once when the dependencies change, it updates. However, methods property updates even if the result doesn't change.
Time to get back to our code
methods: {
addTodo() {
this.todos.push({ id: id++, text: this.newTodo, done: false })
this.newTodo = ''
},
removeTodo(todo) {
this.todos = this.todos.filter(item => item.id !== todo.id)
}
}
}
addTodo()
function adds (actually 'pushes') a new object to the todos
array. Finally, it sets this.newTodo
to an empty string, so we won't see [object Object] weird thing in the input once the form (we will talk about it soon) is submitted.
removeTodo(todo)
function takes a parameter - todo. When 'X' button is clicked, it will bring that particular 'todo' ({ id: id++, text: 'Learn HTML', done: true }... one of these) and filter (remove) it. Since, I've given item
parameter in filter method, each to do will be represented as item
. Logic behind this code; this.todos.filter(item => item.id !== todo.id)
, this compares if item.id
and todo.id
are not the same. Then, it returns items those have different ids.
What the heck does this mean? So, when we click on 'X' button, it returns an object with an id key. As you know filter
method iterates over arrays. So, it iterates over todos
array, and returns item
s those ids don't match todo.id
that comes from the item parameter. part of this code is done, not let's talk about the template part.
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" />
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in hideCompletedTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>
@submit.prevent
prevents forms from refreshing pages when form is submitted. Then, we've got an input tag that is two-way-binded to newTodo data. v-model: is responsible for syncing newTodo
data and addTodo()
function. Outside of our form we have got a list
tag.
Here's how v-for works in a list tag:
As you see there's todo in hideCompletedTodos
. Yes, we've got todos array, but what is todo? It's a local variable that will be alive only in this
todo
is not accesible outside of hideCompletedTodos
array. So, todo
represents each object in hideCompletedTodos
array. Since todo
is a local variable, we can name it however we wish, but understandable naming is one of the best practices. For instance, if I had an array of books
, I would have coded it as <li v-for="book in books" :key="book.id"> ... </li>
.
quick reminder
hideCompletedTodos() {
return this.hideCompleted ? this.todos.filter(item => !item.done) : this.todos
}
The code above returns filtered todos
array or todos
array in its original form, that's why <li v-for="todo in hideCompletedTodos" :key="todo.id">
v-for loop is like this. So, hideCompletedTodos
is either filtered (based on objects done
value property) todos
array or untouched todos array.
<input type="checkbox" v-model="todo.done">
to understand this part, I will share part of the code.
<style>
.done {
text-decoration: line-through;
}
</style>
In short, the code above has a CSS class .done
that outs line through text. This code v-model="todo.done"
binds todo
's done property with done
CSS class. So, when the checkbox has a tick in it, .done
CSS class is added, and a line goes through the text.
How do we get specific todo
from the list by removeTodo(todo)
? This is how:
<li v-for="todo in hideCompletedTodos" :key="todo.id">
<button @click="removeTodo(todo)">X</button>
</li>
As you can notice, todo
is a local variable that represents each object in hideCompletedTodos
('hideCompletedTodos' returns filtered todos
or original todos
array, remmeber?) By using @click
method we trigger removeTodo(todo)
function that takes its parameter from todo
local variable. Their names have to match because todo
local variable is the argument for removeTodo(todo)
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
The code above changes state of hideCompleted
. hideCompleted
is a boolean value, so the code above turns it to the opposite of the current value.
That's it for part 3, we talked about computed properties, and difference between a computed property and a method property in Vue.js.
Next, I will be going through Life Cycle and Template Refs, and maybe Watchers.
Posted on September 1, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.