I want use google map in vue.js.
It's enable when use like vue2-google-map library, but I want more directry use google API.
Output
Demo: https://vue-google-map-provider-sample.netlify.com/
Source: https://github.com/inuscript/example-vue-inject-provide-google-map
Implements
I create those components.
- index.html
- MyMap.vue (Map parent. Combine some components)
- MapLoader.vue (Only process Google Map callback)
- MapProvider.vue (Only use vue.js
provide) - ChildMarker.vue (Example for
markerchild element)
index.html
<!-- index.html -->
<div id="app">
<my-map :markers='[
{"lat":35.6432027,"lng":139.6729435},
{"lat":35.5279833,"lng":139.6989209},
{"lat":35.6563623,"lng":139.7215211},
{"lat":35.6167531,"lng":139.5469376},
{"lat":35.6950961,"lng":139.5037899}
]'>
</my-map>
</div>
It is commonly index.html
MyMap.vue
Component parent example (not so important).
<!-- MyMap.vue -->
<template>
<div>
<h1>Map</h1>
<map-loader
:map-config="mapConfig"
apiKey="YOUR API KEY"
>
<template v-for="marker in markers">
<child-marker :position="marker" />
</template>
</map-loader>
</div>
</template>
<script>
import MapLoader from "./MapLoader.vue"
import ChildMarker from './ChildMarker'
export default {
props: {
markers: Array
},
data(){
return {
mapConfig: {
zoom: 12,
center: this.markers[0]
}
}
},
components: {
MapLoader,
ChildMarker
}
}
</script>
This call map-loader, and use child-marker in internal.
MapLoader
This is the most core implementations (part 1).
<!-- MapLoader.vue -->
<template>
<div>
<div id="map"></div> <!-- point 1 -->
<template v-if="!!this.google && !!this.map"> <!-- point 2 -->
<map-provider
:google="google"
:map="map"
>
<slot/>
</map-provider>
</template>
</div>
</template>
<script>
import GoogleMapsApiLoader from 'google-maps-api-loader'
import MapProvider from './MapProvider'
export default {
props:{
mapConfig: Object,
apiKey: String
},
components: {
MapProvider
},
data(){
return {
google: null,
map: null
}
},
mounted () { // point 3
GoogleMapsApiLoader({
apiKey: this.apiKey
}).then((google) => {
this.google = google
this.initializeMap()
})
},
methods: {
initializeMap (){
const mapContainer = this.$el.querySelector('#map') // point 1
const { Map } = this.google.maps
this.map = new Map(mapContainer, this.mapConfig)
}
}
}
</script>
<style scoped>
#map {
height: 100vh;
width: 100%;
}
</style>
This component some points.
- point 1: This use
#mapand raw DOM. and after it mount withthis.$el - point 2: Core of core. This suspend mount children until
googleormapvalue is valid. This make can useprovide/inject. - point 3: Call
GoogleMapApiLoaderwhenmounted()lifecycle is called.
MapProvider
Core Implementation (part 2)
This setting provide and pass children to google and map value.
Vue's provide is evaluate only first time.
If set provide on map-loader, google and map value may null value and it's not update.
In this implementation, map-loader pass evaluated google and map value, map-provider can valid value.
<!-- MapProvider.vue -->
<template>
<div>
<slot />
</div>
</template>
<script>
export default {
props: {
google: Object,
map: Object,
},
provide() {
return {
google: this.google,
map: this.map
}
},
}
</script>
ChildMarker
<!-- ChildMarker.vue -->
<template></template>
<script>
export default {
inject: ["google", "map"],
props: {
position: Object
},
data(){
return { marker: null}
},
mounted(){
const { Marker } = this.google.maps
this.marker = new Marker({
position: this.position,
map: this.map,
title: "Child marker!"
})
}
}
</script>
Example for Marker child.
It's use inject and use mounted() same as MapLoader
Don't use provide/inject pattern (use slot-scope)
In official document, provide / inject is not recommended generic application.
If don't use provide / inject, we must pass values to children.
In this implementation, we need slot-scope too.
MapLoader remove MapProvider, and pass google and map to slot
<!-- MapLoader.vue -->
<template>
<div>
<div id="map"></div>
<template v-if="!!this.google && !!this.map">
<slot
:google="google"
:map="map"
/>
</template>
</div>
</template>
<script>
:
</script>
Next, in MyApp, use slot-scope and pass value to child-marker
<!-- MyMap -->
<template>
<div>
<h1>Map</h1>
<map-loader
:map-config="mapConfig"
apiKey="YOUR API KEY">
<template slot-scope="scopeProps"> <!-- slot-scope -->
<child-marker
v-for="(marker,i) in markers"
:key="i"
:position="marker"
:google="scopeProps.google"
:map="scopeProps.map"
/>
</template>
</map-loader>
</div>
</template>
ChildMarker remove inject and got value with props.
<!-- ChildMarker -->
<template></template>
<script>
export default {
props: {
google: Object,
map: Object,
position: Object
},
data(){
return { marker: null }
},
mounted(){
const { Marker } = this.google.maps
this.marker = new Marker({
position: this.position,
map: this.map,
title: "Child marker!"
})
}
}
</script>
Top comments (1)
Thanks for this! I was wondering how to get Google Maps to play nice with ES6.