crabbly
crabbly

Reputation: 5186

Vue.js list orderBy filter not sorting dynamically added items to array

When new objects are added to an array of objects in a Vue.js component, the rendered list will update, however, the oderBy filter won't get applied to the new items added. For example, an array of objects where each object have a proerty name, and the orderBy filter is applied to name, if I push a new object to the array, this object will show as the last item of the rendered list. When using unshift, the object will show as the first item of the list.

How do I properly add items to the array where Vue.js can re-render the list applying the orderBy filter to all items?

I tried to play with Vue.set / $set but couldn't make this work. Any help is appreciated.

HTML:

<div v-for="object in array | orderBy 'name'">
    {{ object.name }}
</div>

JS:

data: function() {
    return {
        array: [
            {
                name: 'Jon'
            },
            {
                name: 'Alex'
            }
        ]
    },
},
methods: {
    addItemToArray() {
        let item = { name: 'Paul' }
        this.array.push(item)
    }
}

Upvotes: 1

Views: 2598

Answers (3)

crabbly
crabbly

Reputation: 5186

I just realized that the orderBy filter does work properly when items were pushed to the array. The issue was with the added item format. Of course the app I'm working on is more complex than the example I posted here, and I was missing something. Here is a working fiddle with the same example I posted here: https://jsfiddle.net/crabbly/zr8156b3/

Another thing I wanted to post is that the app I was maintaining uses Vue 1.x, and that filters were removed from Vue.js 2.x, as documented here: https://v2.vuejs.org/v2/guide/migration.html#Replacing-the-orderBy-Filter

That said, the proposed solutions by @King-Reload and @BenCodeZen could be combined as an alternative to create sorted lists (using a computed property that returns the sorted array).

Upvotes: 2

bencodezen
bencodezen

Reputation: 1063

It looks like what you want to do then is to have the rendered list be a computed data instead of using the filter functionality. If I'm not mistaken, filters do not automatically update whereas computed properties would.

So I imagine you would need something along the lines of:

<template>
   <div v-for="person in sortedArray">
       {{ person.name }}
   </div>
</template>

<script>
export default {
   data() {
      return {
         array: [ ... ]
      }
   },
   computed: {
      sortedArray() {
         return orderByName(this.array)
      }
   },
   methods: {
      orderByName(array) {
         ...
      }
   }
}
</script>

Hope this helps!

Upvotes: 2

King Reload
King Reload

Reputation: 2952

methods: {
    array: function (a, b) {
        a = a.name.toLowerCase();
        b = b.name.toLowerCase();

        return a < b ? -1 : a > b ? 1 : 0;
    }

    array: function (a, b) {
        return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
    }
}

I kind of had to combine javascript and vuejs here, but basically why it works is that the comparison < and friends are defined to use lexicographical ordering for strings. Internally it checks each matching character and when it finds two different Unicode values, it returns, which results in "dictionary" ordering. This is also why I added toLowerCase() calls just in case, because the comparison itself compares codepoint values only. It also works with case sensitive sorting.

The Javascript variant looks like this:

array.sort( function( a, b ) {
    a = a.name.toLowerCase();
    b = b.name.toLowerCase();

    return a < b ? -1 : a > b ? 1 : 0;
});

array.sort( function( a, b ) {
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

Upvotes: 0

Related Questions