'Can't load Leaflet inside Vue component
I am trying to create a Leaflet map as a Vue component but I am having some difficult getting started. I installed Leaflet through npm
Where am I going wrong? console.log(Leaflet) is returning a Leaflet object but I am having trouble getting the map to expand and render.
Some direction would be appreciated
<template>
<div id="map"></div>
</template>
<script>
// import leaflet here?
import Leaflet from 'leaflet';
export default {
components: {
Leaflet
},
created() {
console.log(this);
console.log(Leaflet);
},
ready() {
this.map = L.map('map').setView([51.959, -8.623], 14);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map);
}
}
</script>
<style>
#map {
height: 100%;
width: 100%;
margin-top: -24px;
}
/* default legend, layer styling from leaflet template */
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
.legend {
text-align: left;
line-height: 18px;
color: #555;
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
</style>
Solution 1:[1]
There are a couple problems in your code:
- There is not a
ready
lifecycle hook in Vue. Usemounted()
. - You are trying to pass the Leaflet library as a component to Vue, which does nothing since Leaflet is not a Vue component and does not need any sort of registering with Vue.
- A
map
data property was not declared on the Vue component. - There is no problem with importing the library as
Leaflet from 'leaflet'
, but it might be more consistent for yourself or others to declare the Leaflet object asL from 'leaflet'
. You can also use:import { Map } from 'leaflet'
, but then must initialize your Map appropriately:this.map = new Map("mapContainer")
- Prevent potential memory leaks and/or cleanup appropriately by using the
remove()
method on a Leaflet Map Class. A good spot is within the VuebeforeDestroy
lifecycle hook.
Also, don't forget to import the Leaflet CSS, for example:
import "leaflet/dist/leaflet.css";
<template>
<div id="mapContainer"></div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
export default {
name: "LeafletMap",
data() {
return {
map: null
};
},
mounted() {
this.map = L.map("mapContainer").setView([51.959, -8.623], 12);
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map);
},
beforeDestroy() {
if (this.map) {
this.map.remove();
}
}
};
</script>
<style scoped>
#mapContainer {
width: 100vw;
height: 100vh;
}
</style>
Solution 2:[2]
The correct answer was already provided by jhickok but was insightful for the vue3 composition API as well. The leaflet map can't be instantiated in setup()
for the same reasons explained.
<template>
<div id="mapContainer"></div>
</template>
<script>
import { map, tileLayer } from "leaflet";
import { onMounted, onBeforeUnmount } from "vue";
export default {
name: "Map",
setup() {
let container = null;
onMounted(() => {
container = map("mapContainer").setView([51.959, -8.623], 12);
tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(container);
});
onBeforeUnmount(() => {
container.remove();
});
}
};
</script>
<style scoped>
#mapContainer {
width: 40vw;
height: 40vh;
}
</style>
Solution 3:[3]
first install leafletjs and vue2-leaflet with npm
second copy the code to your component
<template>
<div id="app" v-if="isLoad">
<l-map style="height: 350px" :zoom="zoom" :center="center" @click="addMarker" :max-zoom="maxZoom" :min-zoom="minZoom">
<l-tile-layer :url="url" :attribution="attribution"></l-tilelayer>
<l-marker :lat-lng="markerLatLng" ></l-marker>
</l-map>
</div>
</template>
<script>
import {LMap, LTileLayer, LMarker} from "vue2-leaflet";
import "leaflet/dist/leaflet.css";
import {Icon} from "leaflet";
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});
export default {
components: {
LMap,
LTileLayer,
LMarker,
},
mounted() {
this.mapIsReady()
},
data() {
return {
isLoad: false,
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
zoom: 15,
maxZoom:18,
minZoom:5,
center: [35.6892, 51.3890],
markerLatLng: [35.6892, 51.3890],
};
},
methods: {
mapIsReady() {
let self = this
setTimeout(() => self.isLoad = true, 1500)
},
addMarker(event) {
console.log(event)
this.markerLatLng = [event.latlng.lat,event.latlng.lng];
},
},
};
</script>
this code has add marker , max zoom and min zoom for your map
this code used open street map because it's free. you can change it to api.mapbox.com if you want but you need api key or accessToken
Solution 4:[4]
I just did this, but I needed basemaps too (bing).
leafletWrapper.js
const L = require('leaflet')
require('leaflet-bing-layer')
export default L
LeafletMap.vue
<template>
<div id="map">
</div>
</template>
<script>
import L from 'leafletWrapper'
export default {
name: 'leaflet-map',
props: ['center', 'zoom', 'minZoom', 'maxZoom', 'markers'],
data () {
return {
mapObject: {},
markerLayer: {}
}
},
mounted () {
const bingKey = 'foo'
this.mapObject = L.map(
"map", {
center: this.center,
zoom: this.zoom,
minZoom: this.minZoom,
maxZoom: this.maxZoom
}
)
L.tileLayer.bing({bingMapsKey: bingKey, imagerySet: 'Road'}).addTo(this.mapObject)
},
beforeDestroy () {
this.mapObject.clearAllEventListeners()
}
}
</script>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | evendiagram |
Solution 3 | |
Solution 4 | Jon West |