Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update DOM when changes occurs within an object rendered by v-for?

I am using v-for to render a list based on an array of objects.

<ul>
  <li v-for="category, i in categories"
  :class="{active: category.active}"
  @click="changeCategory(i)">
    <a>{{ category.service_type_name }}</a>
  </li>
</ul>

When you click the list item changeCategory(index) runs:

changeCategory(index) {
    this.categories.forEach((c, i) => {
        c.active = (i != index)? false : true
    })
}

So the clicked-on list items active attribute gets set to true and all others set to false, and the list item gets an .active class added to it (because of the :class="{active: category.active}" line in the template.

However, the DOM is not updated to show this change.

Is there anyway to update the DOM automatically when this change occurs, without using this.$forceUpdate() in the changeCategory(index) function?

edit: changed map to forEach, DOM still does not update.

edit: I am using vue-class-components with typescript

import Vue from 'app/vueExt'
import { Component } from 'vue-property-decorator'
import * as Template from './services.vue'

@Component({
    mixins: [Template]
})
export default class Services extends Vue {
    categories = []

    created() {
        this.api.getServiceSetupCatagories().then((res) => {
            this.categories = res.categories
            this.categories.forEach((c, i) => {
                // adding active property to the object
                // active = true if i = 0, false otherwise
                c.active = (i)? false : true
            })
        })
    }

    changeCategory(index) {
        this.categories.forEach((c, i) => {
            c.active = (i != index)? false : true
        })
    }
}
like image 505
wrahim Avatar asked Oct 30 '22 08:10

wrahim


1 Answers

After discussing the issue I suggested the following alternative method.

Add activeIndex to data and change the template to the following.

<ul>
  <li v-for="category, i in categories"
      :class="{active: activeIndex === i}"
      @click="activeIndex = i">
    <a>{{ category.service_type_name }}</a>
  </li>
</ul>

This appears to work for @wrahim.

I believe the underlying issue with the original code was that the active property was added to the category falling into one of Vue's change detection caveats.

like image 73
Bert Avatar answered Nov 14 '22 18:11

Bert