Stephan
Stephan

Reputation: 1868

Vue.js 'v-bind:class' doesn't update even though model does

I've got a list of items, and i want to apply a style to the currently selected one. I'm also using Vuex to manage the state.

My list component:

const List = Vue.component('list', {
    template:
            '<template v-if="items.length > 0">' +
                '<ul class="list-group md-col-12">' +
                    '<a href="#" v-for="(item, index) in items" class="list-group-item list-group-item-action" v-bind:class="{ active: item.isActive }" v-on:click="selectItem(index);">{{ g.text }}</a>' +
                '</ul>' +
            '</template>'
    computed: {
        items: function() {
            return this.$store.state.items;
        }
    },
    methods: {
        selectItem: function (index) {
            this.$store.commit('selectItem', index);
        }
    }
});

My store:

const store = new Vuex.Store({
    state: {
        items: [],
        currentIndex: -1
    },
    mutations: {
        selectItem: function(state, index) {
            if (index === state.currentIndex) {
                return;
            }
            if (state.currentIndex > -1) {
                delete state.items[state.currentIndex].isActive;
            }
            state.currentIndex = index;
            state.items[state.currentIndex].isActive = true;
        }
    }
});

What I see, also using the Vue 'tab' in Chrome DevTools is that whenever I click on an item of the list, the "items" array is being correctly updated, but the class is not set on them.

Also, using the time-travel debugging to go through all the mutations, in that case the class is set.

Any idea why this behavior and how to fix it?

Upvotes: 8

Views: 10557

Answers (2)

Stephan
Stephan

Reputation: 1868

It turns out I should have read the docs more in depth. In particular Change Detection Caveats.

The solution was to change the store mutation thus:

selectItem: function(state, index) {
    if (index === state.currentIndex) {
        return;
    }
    if (state.currentIndex > -1) {
        Vue.delete(state.items[state.currentIndex], 'isActive');
    }
    state.currentIndex = index;
    Vue.set(state.items[state.currentIndex], 'isActive', true);
}

Key here is to use the Vue.delete and Vue.set functions.

Other answer that helped me https://stackoverflow.com/a/40961247/525843

Upvotes: 16

user7135007
user7135007

Reputation:

Try following:

const List = Vue.component('list', {
    template:
            '<template>' +
                '<ul class="list-group md-col-12">' +
                    '<a href="#" v-for="(item, index) in items" class="list-group-item list-group-item-action" v-bind:class="{ active: item.isActive }" v-on:click="selectItem(index);">{{ g.text }}</a>' +
                '</ul>' +
            '</template>'

Upvotes: -2

Related Questions