I have got a mostly functioning self made system for handling google maps in Vue3. I haven't used libraries because none have quite the functionality that I want (eventually), and it seemed relatively straightforward to implement myself (which it has been up until this issue).
The Map component is as follows:
<template>
<div class="map-container">
<div class="google-map" ref="map"></div>
<div>
<slot></slot>
</div>
</div>
</template>
<script>
import { Loader } from '@googlemaps/js-api-loader';
export default {
props: {
latitude: {
type: [String, Number],
default: 0
},
longitude: {
type: [String, Number],
default: 0
},
zoom: {
type: Number,
default: 15
},
},
data(){
return {
map: null,
api: null,
move: null,
}
},
provide: function () {
return {
getMap: this.getMap
}
},
watch: {
latitude(){ this.updateCenter() },
longitude(){ this.updateCenter() }
},
methods: {
getMap(callback){
var self = this;
function checkForMap() {
if (self.map) {
callback(self.map, self.api);
} else {
setTimeout(checkForMap, 50);
}
}
checkForMap();
},
updateCenter(){
let center = new this.api.LatLng(this.latitude, this.longitude);
this.map.panTo(center);
}
},
mounted(){
let self = this
let apiKey = document.getElementById('GOOGLE_MAPS_KEY').value
let loader = new Loader({
apiKey: apiKey,
version: "weekly",
libraries: ["geometry"]
});
loader.load()
.then((google) => {
self.api = google.maps;
self.map = new google.maps.Map(self.$refs.map, {
center: {
lat: parseFloat(self.latitude),
lng: parseFloat(self.longitude)
},
zoom: self.zoom,
disableDefaultUI: true,
});
// Configure the click listener.
self.map.addListener("click", (mapsMouseEvent) => {
self.$emit('click', mapsMouseEvent)
});
// Configure the move listener.
self.map.addListener("bounds_changed", () => {
if(self.move) clearTimeout(self.move);
self.move = setTimeout(() => {
let bounds = self.map.getBounds(true);
let center = self.map.getCenter();
let radius = 100000;
if(bounds && center){
let northEast = bounds.getNorthEast();
radius = self.api.geometry.spherical.computeDistanceBetween(center, northEast);
}
self.$emit('move', {
bounds: bounds,
radius: Math.round(radius),
center: {
latitude: center.lat(),
longitude: center.lng()
}
})
}, 800);
});
})
.catch(e => {});
}
}
</script>
And the marker component is as follows:
<template>
<div></div>
</template>
<script>
export default {
props: {
latitude: String,
longitude: String,
icon: {
type: String,
default: '/images/pin.png'
}
},
data(){
return {
map: null,
api: null,
marker: null
}
},
inject: ["getMap"],
watch: {
latitude(){ this.updatePosition() },
longitude(){ this.updatePosition() }
},
mounted(){
let self = this
this.getMap(function(map, api){
self.map = map;
self.api = api;
self.marker = new api.Marker({
position: new api.LatLng(self.latitude, self.longitude),
map: map,
icon: self.icon,
});
self.marker.addListener("click", () => {
self.$emit('click', self.marker)
});
})
},
beforeUnmount(){
this.marker.setMap(null);
this.marker = null;
},
methods: {
updatePosition(){
this.marker.setPosition( new this.api.LatLng( parseFloat(this.latitude), parseFloat(this.longitude) ) );
}
}
}
</script>
I then have a component that is using these components to display a map with markers on it.
<template>
<div>
<div class="admin-site-map">
<google-map :center="center" :zoom="15">
<map-marker v-for="site in sites" :key="site.id"
:latitude="site.latitude"
:longitude="site.longitude"
@click="markerClicked"
/>
</google-map>
</div>
</div>
</template>
<script>
import GoogleMap from '@/components/Core/Maps/GoogleMap';
import Marker from '@/components/Core/Maps/Marker';
export default {
components: { GoogleMap, mapMarker: Marker },
props: ['sites'],
data(){
return {
center: {lat: 0, lng: 0},
}
},
methods: {
markerClicked(value){
console.log('click: ', value)
}
}
}
</script>
When the list of markers (sites) change, I can see the beforeUnmount
method called on the markers that have been removed, but the marker doesn't disappear from the map, I can still even click on the marker and receive a null
value emitted in the event.
Oddly, calling this.marker.setVisible(false)
does work, and hides the marker.
I've been stuck on this for a while, so any insight or help would be appreciated. Thanks!
CodePudding user response:
Thanks to User28, I eventually found the solution:
On the marker component, marker
(which is where the Google marker object is saved to) should not be a reactive property. Changing the data method to
return {
map: null,
api: null
}
fixed the issue (i.e. remove marker
).