Search code examples
javascriptvue.jsv-forvue-reactivity

v-for not re-rendering array vue js


I have a SPA where I show array of pokemon using v-for, with the option to filter those lists by type or generation. I have a button that clears the filters (sets the type to '' and generation to generation 1), but the v-for loop doesn't re-render the array after the filters are cleared. I've logged the function that returns the array of pokemon to confirm it's working, but Vue JS doesn't render the results. I'm not sure how to proceed.

<div class="pokemon"
            v-for="pokemon in filteredPokemon"
            :key="pokemon.id">
                <h2>{{ pokemon.name }}</h2>
</div>

<script>
import Pokemon from '../pokeData'

export default{
props: ['searchFilters'],
data(){
        return{
            allPokemon: [],
        }
    },
created(){
            this.allPokemon = Pokemon.getPokemon('gen1');
    },
computed: {
        filteredPokemon: function(){ 
            if(this.searchFilters.type){
                if(this.searchFilters.type === ''){
                    return this.allPokemon
                }
                return this.allPokemon.filter(pokemon => {
                    if(pokemon.types.length === 2){
                        if(pokemon.types[0].type.name == this.searchFilters.type || pokemon.types[1].type.name == this.searchFilters.type){
                            return true
                        }
                    }
                    else if(pokemon.types[0].type.name == this.searchFilters.type){
                            return true
                        }
                })
            }
            return this.allPokemon
        }
    },
watch:{
        'searchFilters.generation': function(generation){
            this.allPokemon = Pokemon.getPokemon(generation)
        }
    }
}
}
</script>

Solution

  • farincz is right, you are changing the attributes of allPokemon with the function call to getPokemon and Vue.JS can't find the change (documentation), therefore it's a caveat and you would need to handle this in a different way because Vue doesn't support the way you want it.

    I would filter all pokemons with a filter method with a computed value and bind the filter value to a data property:

    HTML:

    <template>
      <div>
        <textarea v-model="text" name="filter" cols="30" rows="2"></textarea>
        <div class="pokemon" v-for="pokemon in filteredPokemon" :key="pokemon.id">
          <h2>{{ pokemon.name }}</h2>
        </div>
      </div>
    </template>
    

    JS file:

    new Vue({
      el: "#app",  
      data(){
        return{
          text: '',
          pokemons: [
                  {gen: 'gen1', name: 'psyduck', id: '1'},
                  {gen: 'gen1', name: 'charizard', id: '2'},
                  {gen: 'gen1', name: 'pikachu', id: '3'},
                  {gen: 'gen2', name: 'togapi', id: '4'}   
                ]
        }
      },
      computed: {
        filteredPokemon() {
          if(this.text === '') return this.pokemons
          return this.pokemons.filter(x=>x.gen === this.text)
        }
      }
    })
    

    here's the jsfiddle to play around.