Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuejs Leaflet : Map Container Not Found

I am trying to use the leaflet map in Vue, but keep getting the error:

Uncaught Error: Map container not found.

This is what my component looks like:

<template>
  <div id="app" class="container">
    Map
    <div class="col-md-9">
      <div id="mapid"></div>
    </div>
  </div>
</template>

<style scoped>
#mapid { 
    height: 800px;
}
</style>

<script>
import L from 'leaflet'

var mymap = L.map('mapid').setView([51.505, -0.09], 13);

L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={}', {
    attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
    maxZoom: 18,
    id: 'mapbox.streets',
    accessToken: ''
}).addTo(mymap);
</script>

I have also added the Leaflet CSS and JavaScript under that in my index.html head.

like image 831
Kevin Avatar asked Aug 27 '18 22:08

Kevin


1 Answers

Welcome to SO!

When you try instantiating your Leaflet Map by passing it your Element id (L.map('mapid')), the problem is that your Vue component is not yet attached to your page DOM. Therefore when Leaflet tries to query the latter to find your Element, it cannot find it, hence your error message.

Same thing if you try instantiating in the mounted lifecycle hook: your Vue component instance is created and its fragment HTML structure is built, but still not attached to the page DOM.

But instead of passing your Element id, you can directly pass your Element. Note that you still need to do so in the mounted hook, to ensure that your component instance does have an HTML structure built.

L.map(<HTMLElement> el, <Map options> options?)

Instantiates a map object given an instance of a <div> HTML element and optionally an object literal with Map options.

Then to get your Element, simply leverage Vue $refs instance property and ref special attribute, as described in Vue Guide > Accessing Child Component Instances & Child Elements:

[…] sometimes you might still need to directly access a child component in JavaScript. To achieve this you can assign a reference ID to the child component using the ref attribute.

Therefore in your case you would have in your template:

<div id="mapid" ref="mapElement"></div>

And in your component script:

import L from 'leaflet'

export default {
  mounted() {
    var mymap = L.map(this.$refs['mapElement']).setView([51.505, -0.09], 13);
    // etc.
  },
}

The added advantage of using Vue ref over HTML id is that you can have several Vue component instances with their own map, and Vue will reference the appropriate Element to each script.

Whereas with HTML id, if you have several map Elements with same id, Leaflet will get only the first one every time you try to instantiate your map, raising the error that the map is already initialized for this container.

like image 125
ghybs Avatar answered Oct 23 '22 08:10

ghybs