omnomah
omnomah

Reputation: 95

Vue filtering objects property

I have an array of objects like this (continents):

data() {
    return {
        continents: [
            {
                name: "South America",
                countries: [
                    {
                        name: "Paraguay"
                    },
                    {
                        name: "Chile"
                    }
                ]
            },
            {
                name: "North America",
                countries: [
                    {
                        name: "Costa Rica"
                    },
                    {
                        name: "Mexico"
                    }
                ]
            }
        ]
    }
}


// 6 continents and over 250 countries within.

And Im trying to filter it with v-model 'filter' like this

computed: {
    filtered() {
        return this.continents.filter(continent => {
            continent.countries = continent.countries.filter(country => {
                return country.name.match(new RegExp(this.filter, 'i'));
            });
            return continent.countries.length;
        });
    }
}

And render it with v-for directive like this:

<input v-model="filter" type="text">
<div v-for="continent in filtered" v-if="filtered.length" class="countries-group">
    <h4>{{ continent.name }}</h4>
    <ul class="country-list">
        <li v-for="country in continent.countries" class="country-item">{{ country.name }}</li>
    </ul>
</div>

And it almost works, but my computed property filtered modifies the original countries data, so when i'm trying to backspace filter v-model, it not return the inited data because its already overriding with filtered objects.

Upvotes: 1

Views: 2077

Answers (1)

Decade Moon
Decade Moon

Reputation: 34286

my computed property filtered modifies the original contries data

You shouldn't do this. Don't modify state within computed properties because it may trigger other computed properties to be re-evaluated and cause confusing bugs. Computed properties are meant to be pure functions which transforms (without mutating) the component's state in some way.

Try this:

filtered() {
  const filter = this.filter.toLowerCase();

  return this.continents
    .map(continent => Object.assign({}, continent, {
      countries: continent.countries.filter(country => {
        return country.name.toLowerCase().includes(filter);
      }),
    })
    .filter(continent => continent.countries.length);
}

Upvotes: 2

Related Questions