[Solved] Need a (best) way to rerender a v-for list of components


#1

My general setup is v-for list of a form component (made of field components). Those are in a page component in a layout component.

My issue is that when user changes a particular field component of a form component in the list I need to rebuild some options and then make all the rest of the form components in the list update to get those changes.

My page component via an emit knows when it needs to rerender the entire list but I can’t seem to come with a way to do that. I’ve been trying to use vue router to reload the route of the page component (and thus the list) issuing a $router.push on the current page but is seems that is ignored even when I do this <router-view :key="$route.fullPath"></router-view> in the layout component

This blog post http://michaelnthiessen.com/force-re-render/ shows a good method but that’s for a single component not a list. I already have a :key and it’s tied to v-for index so can’t change that to make each form component rerender.

Kinda stuck. Just need some more suggestions to try

<q-list>
    <div class="row no-wrap" v-bind:key="index" v-for="(item, index) in items">
      <q-formc class="col-9" :item="item" :schema="schema" :index="index" @changed="patchItem" @save="saveChanges" @reset="reset"></q-formc>
    </div>
  </q-list>

The @changed provides the way to know when all the q-formc components need to be rerendered.

    async patchItem (patch) {
      console.log(`${this.service} changing value => ${JSON.stringify(patch)} \n ${patch._affectsOptionSets}`)
      let sets = patch._affectsOptionSets
      delete patch._affectsOptionSets
      this.$store.commit(`patch`, [this.service, patch])
      if (sets) {
        console.log('sets', sets)
        sets.forEach(set => {
          console.log('updating option set', set)
          makeOptions(this.$store, set)
        })
       //  NOW THAT THE OPTION SETS ARE UPDATED NEED TO RERENDER EACH QFORMC COMPONENT IN LIST.  WILL NEED TO EVOKE mount() for each.
        console.log(this.$router.currentRoute)
// this doesn't work
       this.$router.push({ path: this.$router.currentRoute.fullPath })
      }
    },

#2

As often happens when I finally give up and ask for help it comes to me how.

The q-list component wraps the list. I only need to force a rerender of it to make the list therein rerender.

so per the blog post http://michaelnthiessen.com/force-re-render/ I set a :key and increment when I need the rerender

<q-list :key="updateList">
    <div class="row no-wrap" v-bind:key="index" v-for="(item, index) in items">
      <q-formc class="col-9" :item="item" :schema="schema" :index="index" @changed="patchItem" @save="saveChanges" @reset="reset"></q-formc>
    </div>
  </q-list>

export default {
  data () {
    return {
      items: [],
      schema: {},
      updateList: 0 // increment to force update
    }
  },

....

    async patchItem (patch) {
      console.log(`${this.service} changing value => ${JSON.stringify(patch)} \n ${patch._affectsOptionSets}`)
      let sets = patch._affectsOptionSets
      delete patch._affectsOptionSets
      this.$store.commit(`patch`, [this.service, patch])
      if (sets) {
        console.log('sets', sets)
        sets.forEach(set => {
          console.log('updating option set', set)
          makeOptions(this.$store, set)
        })
        this.updateList += 1
      }
    },

....

#3

When reactivity is correctly used, a forced re-render shouldn’t be necessary.

My suspicion is that you do something in the patch process that breaks reacivty, i.e.add / remove stuff from a reactive object in way Vue can’t detect.

The above code is a typical indicator that you’re doing something non-reactive elsewhere, so you add a bogus reactive change just to make Vue re-render.

But I don’T know what exactly you do in your store so it’s hard to say.


#4

I do try to say this quite clearly in my post, but I’m afraid that most people ignore this part altogether :frowning: