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
marker
child 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
#map
and raw DOM. and after it mount withthis.$el
- point 2: Core of core. This suspend mount children until
google
ormap
value is valid. This make can useprovide/inject
. - point 3: Call
GoogleMapApiLoader
whenmounted()
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.