vue.js filters in v-for

Mustafa Agamey picture Mustafa Agamey · Apr 23, 2017 · Viewed 11.1k times · Source

I was wondering about using filters in vue

I know computed and when i use it but my question is

i used this code to sort fruits array instead of using computed

<li v-for="fruit in fruits.sort() ">{{fruit }}</li>

it run and i got the result correctly

but console notified me for error

[Vue warn]: You may have an infinite update loop in a component render function. 
(found in <MyFilter> at C:\xampp\htdocs\projects\max\___explaning\169_filters_mixins\src\MyFilter.vue)
warn @ VM6599:564
flushSchedulerQueue @ VM6599:2340
(anonymous) @ VM6599:511
nextTickHandler @ VM6599:460

when i removed .sort() the warn disappeared

the question : why this warn appeared and is there any way to add .sort() to v-for array without using computed values

Answer

kristofferostlund picture kristofferostlund · Apr 23, 2017

You're getting the error because you are creating an infinite loop when calling fruit.sort() in the v-for statement.

fruit.sort() causes the array of be mutated (updated) and when Vue gets notified of this update, it attempts to update the DOM and then evaluate the v-for statement. This again will call fruit.sort() and then trigger an update.

Naive answer:

Instead you could use v-for="fruit in fruits.map(f => f).sort()", although this may become quite heavy if the list is somewhat large. What this does is two things: 1) fruits.map(f => f) creates a new array containing the same values as fruits, and then 2) it sorts the newly created one.

Better answer:

Rather than inlining the copying and sorting inside the template (where it shouldn't be, you could use a method which does the same. You want to put as much logic as possible outside of the templates.

{
    ...Other component properties...

    methods: {
        sorted(arr) {
            // Return a copy of the sorted array
            return arr.map(e => e).sort()
        }
    }

    ...Other component properties...
}

Also better answer:

If you'd been using Vue 1.x, you could have used filters for this (v-for="fruit in fruits | orderBy", but using filters outside of text interpolation ({{ }}) has been removed from Vue 2.x and instead Vue's official migration guide suggests using computed properties for this exact thing.

Now, I'd still suggest not mutating the array inside the computed properties, but instead copy the array first and then sorting it, or perhaps even follow their guide and use lodash's orderBy(...) function for this.

{
    ...Other component properties...

    computed: {
        sortedFruits() {
            // Return a copy of the sorted array
            return this.fruits.map(f => f).sort()
        }
    }

    ...Other component properties...
}

Hope this helps.

Edit: Here's a pen displaying this.