Search code examples
javascriptvue.jsvuejs2vue-componentvuex

Vuex getter to populate a component using v-for


I'm building a vue2 component, with a vuex store object. The component looks like this:

<template>
    <ul id="display">
        <li v-for="item in sourceData()">
            {{item.id}}
        </li>
    </ul>
</template>

<script>  
    export default {
        mounted: function () {
            console.log('mounted')
        },
        computed: {
            sourceData: function() {
                return this.$store.getters.visibleSource
            }
        }
    }
</script>

The store is populated via an ajax call at the beginning of the process, in the main javascript entry:

new Vue({
    store,
    el: '#app',
    mounted: function() {
        this.$http.get('/map/' + this.source_key + '/' + this.destination_key)
            .then(function (response) {
                store.commit('populate', response.data)
            })
            .catch(function (error) {
                console.dir(error);
            });
    }
});

I'm not seeing any errors, and when I use the Vue devtools explorer I can see that my component's sourceData attribute is populated with hundreds of items. I'd expect that once this data is populated, I'd see a bunch of li rows with item.id in them appear on the page.

But despite no errors and apparently good data in the component, I am not seeing the template render anything.

Do I need to use some sort of callback to fire the component after the vuex store is populated?

EDIT: adding store code:

import Vue from 'vue';
import Vuex from 'vuex';
import { getSource, getDestination } from './getters'

Vue.use(Vuex)

export const store = new Vuex.Store({
    state: {
        field_source: [],
        field_destination: []
    },
    getters: {
        visibleSource: state => {
            // this just does some formatting 
            return getSource(state.field_source)
        },
        visibleDestination: state => {
            return getDestination(state.field_destination)
        }
    },
    mutations: {
        populate(state, data) {
            state.field_source = data.source
            state.field_destination = data.destination
        }
    }
})

EDIT2: Maybe it's not a problem with the v-for-- I don't see anything from the template being rendered, not even the main ul tag, which I'd expect to see (empty) even if there was a problem further in the script.


Solution

  • sourceData is a computed property, not a method. You don't need to invoke it. Don't use it like v-for="item in sourceData()", use it like v-for="item in sourceData".

    Other than that, on your 'populate' mutation you are overwritting the observed/reactive objects.

    Either use Vue.set():

    mutations: {
        populate(state, data) {
            // was state.field_source = data.source
            Vue.set(state, 'field_source', data.source);
            // was state.field_destination = data.destination
            Vue.set(state, 'field_destination', data.destination);
        }
    }
    

    Or push all elements to the existing, observed/reactive, arrays:

    mutations: {
        populate(state, data) {
            // was state.field_source = data.source
            state.field_source.push(...data.source);
            // was state.field_destination = data.destination
            state.field_destination.push(...data.destination);
        }
    }