1
votes

I have a Laravel / Vue JS application that integrates a google map component as well as a Vuetify data-table component. The Vue instance also has a computed property that calculates the points for the Google Map, which is based upon another computed property calculated by the Vuetify data-table.

Vue Components code in view blade:

    <v-data-table :ref="model + 'Table'" v-model="selected" :headers="headers" :items="collection" :pagination.sync="pagination" select-all item-key="id" class="elevation-1" >         
    </v-data-table>

    <google-map v-if="mapPoints.length" class="google-map" :markers="mapPoints"></google-map>

Vue Instance code:

var app = new Vue({
el: '#app',
mixins: [ crudFunctionsMixin, adminLayoutMixin ],
data: () => ({
}),
computed: {
    mapPoints: function() {
        return this.calcPoints(this.$refs.prospectTable.filteredItems);
    }
},
methods: {
    calcPoints(items) {
        let myPts = [];
        let i = 0;

        items.forEach(function(prospect) {
            if(prospect.latitude && prospect.longitude) {
                myPts.push({
                    id:     prospect.id,
                    position: {
                        lat: prospect.latitude,
                        lng: prospect.longitude
                    },
                    index: i++,
                    title:  prospect.address + ", " + prospect.city + ", " + prospect.zip,
                    icon:   "\/img\/numberedMapIcons\/number_" + prospect.id + ".png"
                });
            }
        });

        return myPts;
    }
}
});

This doesn't work. It throws a type error:

TypeError: Cannot read property 'filteredItems' of undefined

This is because the filteredItems computed property within the Vuetify data-table component has not been calculated yet at the time the GoogleMap component evaluates it.

If i comment out the GoogleMap component that uses the "points" computed property, and evaluate the points property using Vue JS Devtools after page render, the points property data is calculated correctly. This is because the computed property is not evaluated for the first time until you access it with Devtools, so the page has already been rendered. So the "points" computed property evaluates fine after page render, but at the time the GoogleMap conponents tries to first calculate it.


I next tried to modify the "points" computed property to return an empty array if "$refs.prospectTable.filteredItems" was still undefined.

computed: {
    mapPoints: function() {
        if(!this.$refs.prospectTable)
            return [];
        else
            return this.calcPoints(this.$refs.prospectTable.filteredItems);
    }
}

However using this approach the computed property is only evaluated once, before $refs.prospectTable.filteredItems is defined, so it returns the empty array. The computed property is never evaluated again, even after $refs.prospectTable.filteredItems changes.


Why is the "mapPoints" computed property not recalculated each time "$refs.prospectTable.filteredItems" changes like it did when it never returned an initial empty array?

computed: {
    mapPoints: function() {
        return this.calcPoints(this.$refs.prospectTable.filteredItems);
    }
}

UPDATE:

Per @larizzatg advice, changing mapPoints from a computed property to a $watch variable inside mounted() did the trick:

mounted() {
    this.$watch(
        () => {
            return this.$refs.prospectTable.filteredItems;
        },
        (items) => {
            this.mapPoints = this.calcPoints(items);
        }
    );
}
1

1 Answers

2
votes

When the vue app is created the computed properties are executed. At this point, the app doesn't have the component in the real dom (the ref keyword functions like a getElementById because prostectTable is not defined you get undefined)

In the mounted lifecycle the components are in the dom so you can access the ref. Instead of a computed property add a watcher to the refs prospectTable