Sorting a List using Computed Properties in VueJS

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.

tgugnani: Web Stuff Enthusiast.