I am really struggling with the structure of the new Google Maps Javascript API.
In the tutorial for adding a map with a marker, the new structure wants to async
load the library, to load the relevant class(es):
const { Map } = await google.maps.importLibrary("maps");
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
OK, so I wrap this in some sort of async
method to init the map
const initMap = async () => {
const { Map } = await google.maps.importLibrary("maps");
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
// ...
}
No problem.. but what happens if I want another method to add or manipulate a marker? Do I call importLibrary
again?
const addMarker = async (blah) => {
// Will this hit some sort of local cache of types??
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
// ...
}
I mean.. is this thing cached, or is it going to try to import code from the interweb every time? Why do I have to async
load just to get a class definition?
There is no top-level await
in the browser (yet), so the load of these classes needs to be scoped to a function
, which means they are not accessible outside that scope, like.. another function.
How are people doing this? The whole structure of this API seems bonkers to me.
What I want to do is something like this:
// This won't work, but this is what I want to do:
const { Map } = await google.maps.importLibrary("maps");
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
class GoogleMap {
constructor() {
this._map;
this._markers = [];
}
init(elem) {
this._map = new Map(elem, {...});
}
addMarker(position) {
this._markers.push(new AdvancedMarkerElement({
this._map,
position: position,
}));
}
}
What is the best-practice guidance for using this [bonkers] API in this way?
Admittedly, the new Marker API is a bit dorky.
Still, the dynamic loading scripts are indeed cached. You can verify this by checking the Network tab and looking for https://maps.googleapis.com/maps-api-v3/api/js/.../marker.js
:
const { Map } = await google.maps.importLibrary("maps");
let AdvancedMarkerElement;
({ AdvancedMarkerElement } = await google.maps.importLibrary("marker"));
// some logic
({ AdvancedMarkerElement } = await google.maps.importLibrary("marker"));
({ AdvancedMarkerElement } = await google.maps.importLibrary("marker"));
Secondly, by calling .importLibrary()
, you're accessing the global google.maps
namespace. Calling importLibrary("marker")
then populates the google.maps.marker
library. That's all there's to it.
Once loaded, these two calls are equivalent:
const m1 = new google.maps.marker.AdvancedMarkerElement(...);
const m2 = new AdvancedMarkerElement(...);
That said, you can write your own utility that wraps around the google.maps.marker
library to avoid the verbose dot paths…
And this brings me to an official utility package (@googlemaps/adv-markers-utils
) that's poised to "simplify common patterns for using Advanced Markers."
With it, you only import the marker
library once (but it's got to be in an async context):
async function main() {
const {Map} = await google.maps.importLibrary('maps');
// Notice that we don't care about the return value of this statement -- it only populates `google.maps.marker`
await google.maps.importLibrary('marker');
}
Afterwards, you are free to use the synchronous initializer throughout your app:
const marker = new Marker({
position: {lat: 53.5, lng: 10.05},
map
});
Do check out the related playground too.