In this exercise, we will learn how we can sort a list of objects using Computed Properties.
Consider a VueJS instance, with an array of objects with three data properties.
products: [
{ name: "Keyboard", price: 44, category: 'Accessories'},
{ name: "Mouse", price: 20, category: 'Accessories'},
{ name: "Monitor", price: 399, category: 'Accessories'},
{ name: "Dell XPS", price: 599, category: 'Laptop'},
{ name: "MacBook Pro", price: 899, category: 'Laptop'},
{ name: "Pencil Box", price: 6, category: 'Stationary'},
{ name: "Pen", price: 2, category: 'Stationary'},
{ name: "USB Cable", price: 7, category: 'Accessories'},
{ name: "Eraser", price: 2, category: 'Stationary'},
{ name: "Highlighter", price: 5, category: 'Stationary'}
]
We use the for loop to display the list in the table format in our HTML.
<table border="1">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Category</th>
</tr>
</thead>
<tbody>
<tr v-for="product in sortedProducts">
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.category}}</td>
</tr>
</tbody>
</table>
The result looks like below.
Next, we define two new data properties in our Vue instance.
sortBy: 'name',
sortDirection: 'asc',
As the name suggests, sortBy
variable keeps the track of which key we are looking to sort by, By default, it's the name property of the product object.
sortDirection
keeps track of the sort order, whether ascending or descending, by default, it's asc
.
We want to sort the items in the table by capturing the click event on the header row of the table.
Let's catch the click event on each row of the table.
<th @click="sort('name')">Name</th>
<th @click="sort('price')">Price</th>
<th @click="sort('category')">Category</th>
With the click
event, we invoke the sort method and pass on the appropriate sort key to the method.
Let's add the sort method to our Vue Instance.
methods: {
sort: function(s){
if(s === this.sortBy) {
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
}
this.sortBy = s;
}
},
Inside this function, we change the sortBy key and if the passed key is the same as the current sortBy key then we change the sortDirection
.
Now since we have the ability to change the sortBy
key and sortDirection
, we can now write the computed property which will sort the array of objects as soon as the dependent property changes.
computed: {
sortedProducts: function(){
return this.products.sort((p1,p2) => {
let modifier = 1;
if(this.sortDirection === 'desc') modifier = -1;
if(p1[this.sortBy] < p2[this.sortBy]) return -1 * modifier; if(p1[this.sortBy] > p2[this.sortBy]) return 1 * modifier;
return 0;
});
}
},
We created a new computed property named sortedProducts
, inside this, we call the sort method on the array. Inside the sort method, we pass a callback function which takes in the sorting logic. The callback function either returns 1 or -1 depending upon the sortDirection
.
We change the v-for
directive to work on sortedProducts
property instead or products
property.
Now when you click on the header name of the table, it should sort the data by that particular property and if you click it again it will change the sortDirection
.
We should also, denote the sortBy
and sortDirection
by putting the arrow key next to the name of the property.
<th @click="sort('name')" v-bind:class="[sortBy === 'name' ? sortDirection : '']">Name</th>
<th @click="sort('price')" v-bind:class="[sortBy === 'price' ? sortDirection : '']">Price</th>
<th @click="sort('category')" v-bind:class="[sortBy === 'category' ? sortDirection : '']">Category</th>
We add a new v-bind
directive which puts the direction of the sort on the active sortBy
header element.
Add the following CSS rules in your HTML to show the appropriate arrow next to the name of the key.
.asc:after{
content: "\25B2"
}
.desc:after{
content: "\25BC"
}
You should now see the arrow in the header.