This is my first VueJS project and I've got vue2-google-maps up and running but I've e across an issue when I attempt to connect the map markers to my site's JSON feed (using the Wordpress REST API), the Lat and Lng values are returning undefined or NaN.
On further investigation (thanks to @QuỳnhNguyễn below) it seems like the Google Maps instance is being run before the data is ready. I have tried watching for the feed to be loaded before initialising the map, but it doesn't seem to work.
The marker locations are pulled in from the WordPress REST API using JSON and exist in an array (locations). The array is present and populated in Vue Dev Tools (51 records), but when checking on mounted, the array is empty. The data is pulled in at the created stage, so I don't know why it wouldn't be ready by the mounted stage.
The code in question is as below...
Template:
<template>
<gmap-map v-if="feedLoaded" ref="map" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker
:key="index" v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.place_latitude), lng: parseFloat(m.place_longitude) }"
@click="toggleInfoWindow(m,index)"
:icon="mapIconDestination">
</gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
</template>
Script
<script>
const axios = require('axios');
const feedURL = "API_REF";
export default {
props: {
centerRef: {
type: Object,
default: function() {
return { lat: -20.646378400026226, lng: 116.80669825605469 }
}
},
zoomVal: {
type: Number,
default: function() {
return 11
}
}
},
data: function() {
return {
feedLoaded: false,
zoom: this.zoomVal,
center: this.centerRef,
options: {
mapTypeControl: false,
streetViewControl: false,
},
mapTypeId: 'styledMapType',
mapIconDestination: '/images/map-pin_destination.png',
mapIconActivity: '/images/map-pin_activity.png',
mapIconAcmodation: '/images/map-pin_acmodation.png',
mapIconEvent: '/images/map-pin_event.png',
mapIconBusiness: '/images/map-pin_business.png',
locations: [],
markers: []
}
},
created: function() {
this.getData();
},
mounted: function() {
this.$nextTick(() => {
this.$refs.karrathaMap.$mapPromise.then((map) => {
var styledMapType = new google.maps.StyledMapType(
[...MAP_STYLE SETTINGS...]
)
map.mapTypes.set('styled_map', styledMapType);
map.setMapTypeId('styled_map');
})
});
},
watch: {
feedLoaded: function() {
if (this.feedLoaded == true) {
console.log(JSON.stringify(this.locations))
}
}
},
methods: {
getData() {
const url = feedURL;
axios
.get(url)
.then((response) => {this.locations = response.data;})
.then(this.feedLoaded = true)
.catch( error => { console.log(error); }
);
}
}
}
</script>
This is my first VueJS project and I've got vue2-google-maps up and running but I've e across an issue when I attempt to connect the map markers to my site's JSON feed (using the Wordpress REST API), the Lat and Lng values are returning undefined or NaN.
On further investigation (thanks to @QuỳnhNguyễn below) it seems like the Google Maps instance is being run before the data is ready. I have tried watching for the feed to be loaded before initialising the map, but it doesn't seem to work.
The marker locations are pulled in from the WordPress REST API using JSON and exist in an array (locations). The array is present and populated in Vue Dev Tools (51 records), but when checking on mounted, the array is empty. The data is pulled in at the created stage, so I don't know why it wouldn't be ready by the mounted stage.
The code in question is as below...
Template:
<template>
<gmap-map v-if="feedLoaded" ref="map" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker
:key="index" v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.place_latitude), lng: parseFloat(m.place_longitude) }"
@click="toggleInfoWindow(m,index)"
:icon="mapIconDestination">
</gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
</template>
Script
<script>
const axios = require('axios');
const feedURL = "API_REF";
export default {
props: {
centerRef: {
type: Object,
default: function() {
return { lat: -20.646378400026226, lng: 116.80669825605469 }
}
},
zoomVal: {
type: Number,
default: function() {
return 11
}
}
},
data: function() {
return {
feedLoaded: false,
zoom: this.zoomVal,
center: this.centerRef,
options: {
mapTypeControl: false,
streetViewControl: false,
},
mapTypeId: 'styledMapType',
mapIconDestination: '/images/map-pin_destination.png',
mapIconActivity: '/images/map-pin_activity.png',
mapIconAcmodation: '/images/map-pin_acmodation.png',
mapIconEvent: '/images/map-pin_event.png',
mapIconBusiness: '/images/map-pin_business.png',
locations: [],
markers: []
}
},
created: function() {
this.getData();
},
mounted: function() {
this.$nextTick(() => {
this.$refs.karrathaMap.$mapPromise.then((map) => {
var styledMapType = new google.maps.StyledMapType(
[...MAP_STYLE SETTINGS...]
)
map.mapTypes.set('styled_map', styledMapType);
map.setMapTypeId('styled_map');
})
});
},
watch: {
feedLoaded: function() {
if (this.feedLoaded == true) {
console.log(JSON.stringify(this.locations))
}
}
},
methods: {
getData() {
const url = feedURL;
axios
.get(url)
.then((response) => {this.locations = response.data;})
.then(this.feedLoaded = true)
.catch( error => { console.log(error); }
);
}
}
}
</script>
Share
Improve this question
edited Dec 29, 2018 at 15:27
AlxTheRed
asked Dec 27, 2018 at 13:51
AlxTheRedAlxTheRed
5356 silver badges23 bronze badges
11
-
1
Please try this:
:position="google && new google.maps.LatLng(parseFloat(m.place_latitude), parseFloat(m.place_longitude))"
– Quynh Nguyen Commented Dec 29, 2018 at 6:07 -
If it's not work please try to hardcode
lat, lng
via a number – Quynh Nguyen Commented Dec 29, 2018 at 6:12 - @QuỳnhNguyễn Won't that create a new instance of the map for each marker? Also, it works with hard-coded values. – AlxTheRed Commented Dec 29, 2018 at 6:39
-
No it's only check
google
is available for create new marker. – Quynh Nguyen Commented Dec 29, 2018 at 6:44 - @QuỳnhNguyễn I've tried your code and it es back with position: undefined. I cannot use hard-coded values as the data es from the WordPress REST API (JSON feed). – AlxTheRed Commented Dec 29, 2018 at 6:45
3 Answers
Reset to default 4It turns out the issue was dirty data.
The JSON response was including locations that were not supposed to be included on the map, so it failed every time it came across an entry that didn't include the ACF fields, despite me setting the feed to only return data where include on map was true.
I have solved the issue by handling the data once the feed is loaded and creating a new array (markers) from it using the valid data, then using this, rather than the original (locations) array to place the markers on the map.
Cleaning the data:
watch: {
feedLoaded: function() {
if (this.feedLoaded == true) {
var LocationList = this.locations;
for (var i = 0; i < LocationList.length; i++) {
var includeOnMap = LocationList[i].acf['place_include-on-map'];
if (includeOnMap === true) {
var placeName = LocationList[i].title.rendered;
var placeDescription = LocationList[i].acf['place_short-description'];
var placeLatitude = LocationList[i].acf['place_latitude'];
var placeLongitude = LocationList[i].acf['place_longitude'];
var placeIcon = this.mapIconDestination;
this.markers.push({ name: placeName, lat: placeLatitude, lng: placeLongitude, icon: placeIcon });
}
}
}
}
}
Then, the gmap ponent:
<gmap-map ref="karrathaMap" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker v-if="feedLoaded == true" :key="index" v-for="(m, index) in markers" :position="{ lat: parseFloat(m.lat), lng: parseFloat(m.lng) }" @click="toggleInfoWindow(m,index)" :icon="m.icon"></gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
Thank you everybody who contributed to helping me get to the bottom of the issue. I will now spend some time rethinking how the data is structured.
It appears to be related with data format. According to vue-devtools
from provided screenshot your data is returned from WordPress REST API in the following format:
[
{
"acf": {
"place_latitude": "-22.695754",
"place_longitude": "118.269081",
"place_short-description": "Karijini National Park"
},
"id": 12,
"parent": 10,
"title": {
"rendered": "Karijini National Park"
}
},
...
]
Having how locations
array is getting initialized (getData
method), position property could be passed like this:
<gmap-marker
:key="index"
v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.acf.place_latitude), lng: parseFloat(m.acf.place_longitude) }"
></gmap-marker>
Here is a demo
vuejs supports v-if directive on elements. I remend you try as following code snippet.
<template>
<div id="map" v-if="loaded">
<gmap-map ref="map" :center="center" :zoom="zoom" :map-type-id="mapTypeId" :options="options">
<gmap-marker
:key="index" v-for="(m, index) in locations"
:position="{ lat: parseFloat(m.place_latitude), lng: parseFloat(m.place_longitude) }"
@click="toggleInfoWindow(m,index)"
:icon="mapIconDestination">
</gmap-marker>
<gmap-info-window></gmap-info-window>
</gmap-map>
</div>
</template>
<script>
export default {
data() {
return {
loaded: false
}
},
beforeMount: function () {
const url = feedURL;
axios
.get(url)
.then((response) => {
this.locations = response.data;
//activate element after api call response recieved
this.loaded = true
})
.catch(error => {
console.log(error);
}
);
}
}
</script>