Closed JoseGoncalves closed 5 months ago
As I needed to test this change on a live app, I've published it as @josempgon/vue3-google-map.
I would prefer not to have to maintain code related to removing the google API script. This is better handled at the API script loader level. @googlemaps/js-api-loader
has some long standing issues related to this (e.g. https://github.com/googlemaps/js-api-loader/issues/100).
We could alternatively switch to another library if it could handle that. In the meantime we have an API that allows loading the script externally so the user could write a wrapper component for GoogleMap
that handles loading/reloading the script:
https://github.com/inocan-group/vue3-google-map/issues/99#issuecomment-1237395931
Hi @HusamIbrahim, I have done it firstly using the apiPromise
prop and the @googlemaps/js-api-loader
package to be able to unload/load the maps API, but I find it somehow cumbersome to do that. Someone that uses a Vue component that has a language
prop would expect that, when that prop is changed, the language is changed in the interface, without needing to know what is done internaly in the library to handle that change.
I agree it is cumbersome but like I said I'd rather not maintain code like this, especially considering the use case is not that common. This is better handled at the loader lib level so I'm happy to migrate if there is an alternative that handles this. Otherwise we do have apiPromise
as a workaround, cumbersome as it may be.
On managing user expectations we could add to the docs that these props are non-reactive.
I understand you wouldn't like to maintain this kind of functionality, just don't agree with you as it's an unusual use case. At least in non-english speaking countries, it's usual to have to design multilingual apps and, if that app contains a map, we would expect the map language is in sync with the app language.
At least it would be nice to have documentation on how to implement the language switch with the available API.
No harm in adding that to the docs. I'm happy to accept another PR if you'd like to share the recipe you used here.
Hi @HusamIbrahim. I've managed to do it with a wrapper component for GoogleMap
like this:
<script setup>
import { nextTick, ref, watch } from 'vue';
import { GoogleMap } from 'vue3-google-map';
import { Loader } from '@googlemaps/js-api-loader';
import { useState } from '@/composables/state';
const props = defineProps({
apiKey: {
type: String,
default: ''
},
version: {
type: String,
default: 'weekly'
},
region: {
type: String,
default: undefined
},
language: {
type: String,
default: undefined
},
libraries: {
type: Array,
default: () => ['places']
}
});
const emit = defineEmits(['map-ready']);
const { mapsLoader } = useState();
const apiPromise = ref(null);
const mapRef = ref(null);
const loadMaps = () => {
const { apiKey, version, region, language, libraries } = props;
mapsLoader.value = new Loader({
apiKey,
version,
region,
language,
libraries
});
apiPromise.value = mapsLoader.value.load();
};
const unloadMaps = () => {
const nodes = document.querySelectorAll(
'script[src*="maps.googleapis.com"], link[href*="fonts.googleapis.com"]'
);
nodes.forEach(el => {
if (el.parentNode) el.parentNode.removeChild(el);
});
delete window.google.maps;
Loader.instance = null;
};
watch(
() => mapRef.value?.ready,
ready => {
if (ready) emit('map-ready', mapRef.value);
}
);
watch(
() => [props.region, props.language],
async () => {
mapsLoader.value = null;
await nextTick();
unloadMaps();
loadMaps();
}
);
if (mapsLoader.value) {
const { language, region } = mapsLoader.value.options;
if (language !== props.language || region !== props.region) {
unloadMaps();
loadMaps();
} else {
apiPromise.value = mapsLoader.value.load();
}
} else {
loadMaps();
}
</script>
<template>
<GoogleMap
v-if="mapsLoader"
ref="mapRef"
:api-promise="apiPromise"
>
<slot />
</GoogleMap>
</template>
What do you think of this approach?
The useState()
composable used to store the map loader instance was implemented with the createGlobalState function from the VueUse library. Do you think that composable should be added also to the docs?
Yeah that might be a bit too long for the docs. Maybe you could create an example in https://vite.new/vue and just link to it. Make sure it's frozen so it doesn't get updated.
Being a bit long was why I've tried to push that functionality inside to the main GoogleMap
component.
Sorry, I currently don't have time to setup an example in https://vite.new/vue.
Being a bit long was why I've tried to push that functionality inside to the main
GoogleMap
component.Sorry, I currently don't have time to setup an example in https://vite.new/vue.
That's perfectly fine we can always point people to your example here if they ever need this.
While Google Maps does not provide a way to dynamicaly change the interface language, the only way to do it is by unloading/loading the map API. This PR does this when language and region props are changed.