VueJS:Google 在数据准备好之前加载地图 - 如何让它等待? (Nuxt)
VueJS: Google Maps loads before data is ready - how to make it wait? (Nuxt)
这是我的第一个 VueJS 项目,我已经 vue2-google-maps up 和 运行ning 但我遇到了一个当我尝试将地图标记连接到我网站的 JSON 提要(使用 Wordpress REST API)、Lat 和 Lng[ 时出现问题=35=] 值返回 undefined 或 NaN.
经过进一步调查(感谢下面的@QuỳnhNguyễn),似乎 Google 地图实例在数据准备好之前 运行。我试过在初始化地图之前观察要加载的提要,但它似乎不起作用。
标记位置是使用 JSON 从 WordPress REST API 提取的,并存在于数组(位置)中。该数组存在并填充在 Vue Dev Tools(51 条记录)中,但在检查 mounted 时,该数组为空。数据是在 created 阶段拉进来的,所以我不知道为什么它不会在 mounted 阶段准备好。
问题代码如下...
模板:
<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>
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',
mapIconAccommodation: '/images/map-pin_accommodation.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>
vuejs 支持元素上的 v-if 指令。我建议您尝试以下代码片段。
<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>
好像和数据格式有关。根据来自 provided screenshot 的 vue-devtools
,您的数据以下列格式从 WordPress REST API 返回:
[
{
"acf": {
"place_latitude": "-22.695754",
"place_longitude": "118.269081",
"place_short-description": "Karijini National Park"
},
"id": 12,
"parent": 10,
"title": {
"rendered": "Karijini National Park"
}
},
...
]
有了 locations
数组的初始化方式(getData
方法),位置 属性 可以像这样传递:
<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>
原来是脏数据问题。
JSON 响应包含不应该包含在地图上的位置,因此每次遇到不包含 ACF 字段的条目时都会失败,尽管我设置了提要仅 return 地图上包含的数据为真。
我已经解决了这个问题,方法是在加载提要后处理数据并使用有效数据从中创建一个新数组(标记),然后使用它而不是原始(位置)数组来放置标记在地图上。
正在清理数据:
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 });
}
}
}
}
}
然后,gmap 组件:
<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>
感谢所有帮助我弄清问题真相的人。我现在将花一些时间重新思考数据的结构。
这是我的第一个 VueJS 项目,我已经 vue2-google-maps up 和 运行ning 但我遇到了一个当我尝试将地图标记连接到我网站的 JSON 提要(使用 Wordpress REST API)、Lat 和 Lng[ 时出现问题=35=] 值返回 undefined 或 NaN.
经过进一步调查(感谢下面的@QuỳnhNguyễn),似乎 Google 地图实例在数据准备好之前 运行。我试过在初始化地图之前观察要加载的提要,但它似乎不起作用。
标记位置是使用 JSON 从 WordPress REST API 提取的,并存在于数组(位置)中。该数组存在并填充在 Vue Dev Tools(51 条记录)中,但在检查 mounted 时,该数组为空。数据是在 created 阶段拉进来的,所以我不知道为什么它不会在 mounted 阶段准备好。
问题代码如下...
模板:
<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>
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',
mapIconAccommodation: '/images/map-pin_accommodation.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>
vuejs 支持元素上的 v-if 指令。我建议您尝试以下代码片段。
<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>
好像和数据格式有关。根据来自 provided screenshot 的 vue-devtools
,您的数据以下列格式从 WordPress REST API 返回:
[
{
"acf": {
"place_latitude": "-22.695754",
"place_longitude": "118.269081",
"place_short-description": "Karijini National Park"
},
"id": 12,
"parent": 10,
"title": {
"rendered": "Karijini National Park"
}
},
...
]
有了 locations
数组的初始化方式(getData
方法),位置 属性 可以像这样传递:
<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>
原来是脏数据问题。
JSON 响应包含不应该包含在地图上的位置,因此每次遇到不包含 ACF 字段的条目时都会失败,尽管我设置了提要仅 return 地图上包含的数据为真。
我已经解决了这个问题,方法是在加载提要后处理数据并使用有效数据从中创建一个新数组(标记),然后使用它而不是原始(位置)数组来放置标记在地图上。
正在清理数据:
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 });
}
}
}
}
}
然后,gmap 组件:
<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>
感谢所有帮助我弄清问题真相的人。我现在将花一些时间重新思考数据的结构。