Getting Google Maps to work with Vue.js

2019-08-16 17:56发布

I'm trying to get Google Maps working with Vue.js.

This is the HTML I am using. Assume that v-if="activeStep === 1" works and is properly displaying the intended div:

<div id="map-container" v-if="activeStep === 1">
  <div id='location-map'></div>
</div>
...
<script src="https://maps.googleapis.com/maps/api/js?key=MYKEY"></script>

id styling:

#location-map { height: 250px; }
#map-container { padding: 15px 0; }

and my Vue binding:

if(document.getElementById("map-container")) {
  const mapVue = new Vue({
    el: '#map-container',
    data: {
      activeStep: 1,
      defaultLatLng: {lat: 37.5665, lng: 126.9780}
    },
    mounted: function() {
      var map = new google.maps.Map(document.getElementById('location-map'), {
        zoom: 15,
        center: this.defaultLatLng,
        scrollwheel: false,
        disableDoubleClickZoom: true,
        panControl: false,
        streetViewControl: false,
        draggable: false
      });
      var marker = new google.maps.Marker({
        position: this.defaultLatLng,
        map: map
      });
    }
  })
}

But the map is not displaying. Am I doing something incorrectly here?

There are two issues I can think of:

  1. The Google Maps documentation calls the script with a &callback=initMap and names the function initMap whereas I haven't done that. Instead, I am having the function call occur with vue's mounted directive. Not sure how I'd call initMap from a vue method. Also, I am not using async defer (like <script async defer src="https://maps.googleapis.com/maps/api/js?key=MYKEY"></script>) on the script as I read somewhere this was not necessary.

  2. The v-if actually removes the element and displays it depending on the activeStep, so perhaps the map function isn't re-initialized when that part of the DOM is generated?

Any help would be much appreciated. Thanks in advance!

4条回答
够拽才男人
2楼-- · 2019-08-16 18:01

If you are not using async defer with a callback, the order of your Javascript will be important.

And it will also matter if you do use async/defer for other scripts. I suggest using async defer with a callback to ensure that Google maps has loaded properly and so you don't have to worry about order.

If you want to hook up your callback to Vue you can do something like this:

Make your callback point to a global Vue instance:

&callback=app.createMap

Create a global Vue instance like so:

window.app = new Vue({});

Now you can simply have a createMap method on your main Vue instance where you can initialize your map.

Of course if you are not showing your map container straight away and you display it later on you will have to call a resize event on your map to ensure it re-renders.

查看更多
Evening l夕情丶
3楼-- · 2019-08-16 18:17

I resolved it by creating a custom directive and binding the initialization script to an inserted hook:

Vue.directive('map', {
  // When the bound element is inserted into the DOM:
  inserted: function (el, binding) {
    var map = new google.maps.Map(document.getElementById('location-map'), {
      zoom: 15,
      draggable: false,
      panControl: false,
      scrollwheel: false,
      streetViewControl: false,
      center: binding.value,
      disableDoubleClickZoom: true
    });
    var marker = new google.maps.Marker({
      map: map,
      position: binding.value
    });
  }
})

template:

<strong>Map</strong><br>
<div id="map-container" v-map="{lat: coords_here, lng: coords_here}">
  <div id='location-map'></div>
</div>

I think the problem was that v-if creates and destroys the actual HTML so the element was not being re-initialized or something along those lines. This seems to do the trick.

查看更多
唯我独甜
4楼-- · 2019-08-16 18:18

Im pretty sure you havto set a default height in order for it to render.

查看更多
我命由我不由天
5楼-- · 2019-08-16 18:22

I know it's an old question but if someone has the same problem ... use v-show instead of v-if.

查看更多
登录 后发表回答