web-dev-qa-db-fra.com

VueJS: Google Maps se charge avant que les données soient prêtes - comment le faire attendre? (Nuxt)

Ceci est mon premier projet VueJS et vue2-google-maps est opérationnel mais j'ai rencontré un problème lorsque je tente de connecter les marqueurs de carte au flux JSON de mon site (à l'aide de Wordpress REST API), les valeurs Lat et Lng renvoient undefined ou NaN .

Une enquête plus approfondie (grâce à @ QuỳnhNguyễn ci-dessous) semble indiquer que l’instance de Google Maps est en cours d’exécution avant que les données ne soient prêtes. J'ai essayé de surveiller le chargement du flux avant d'initialiser la carte, mais cela ne semble pas fonctionner.

Les emplacements des marqueurs sont extraits de l'API WordPress REST utilisant JSON et existent dans un tableau (emplacements). Le tableau est présent et rempli dans Vue Dev Tools (51 enregistrements), mais lors de la vérification de monté , le tableau est vide. Les données sont extraites au stade créé , donc je ne sais pas pourquoi elles ne seraient pas prêtes par le plateau monté.

Le code en question est comme ci-dessous ...

Modèle:

<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',
                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>
6
AlxTheRed

Il s'avère que le problème était des données sales.

La réponse JSON incluait des emplacements qui n'étaient pas supposés être inclus sur la carte. Par conséquent, elle échouait chaque fois qu'une entrée n'incluait pas les champs ACF, malgré le fait que le flux ne renvoie que les données où inclure sur la carte vrai.

J'ai résolu le problème en traitant les données une fois le flux chargé et en créant un nouveau tableau (marqueurs) à l'aide des données valides, puis en utilisant ce tableau plutôt que le tableau d'origine (emplacements) pour placer les marqueurs sur la carte.

Nettoyage des données:

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 });
                }

            }
        }
    }
}

Ensuite, le composant 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>

Merci à tous ceux qui ont contribué à m'aider à aller au fond des choses. Je vais maintenant passer un peu de temps à repenser la structure des données.

4
AlxTheRed

Il semble être lié au format de données. Selon vue-devtools de capture d'écran fournie vos données sont renvoyées à partir de l'API WordPress REST au format suivant:

[
  {
    "acf": {
      "place_latitude": "-22.695754",
      "place_longitude": "118.269081",
      "place_short-description": "Karijini National Park"
    },
    "id": 12,
    "parent": 10,
    "title": {
      "rendered": "Karijini National Park"
    }
  },
  ... 
]

Ayant la manière dont le tableau locations est initialisé (méthode getData), la propriété position peut être passée comme ceci: 

<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>

Voici une démo

3
Vadim Gremyachev

vuejs prend en charge la directive v-if sur les éléments. Je vous recommande d'essayer comme extrait de code suivant.

<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>
0
Saeed Alizadeh