Hi everyone,
I’m trying to embed a google map in Wix via HTML component.
In order to work, It needs first to load the google.map library and only once the library is loaded it can be used. I use the following code to message Wix once the HTML component is initiated
//HTML component code **simplied**
<html>
<body>
<div id="map" style="height: 100%; width: 100%;" />
<script type="text/javascript">
EVENT_READY = "mapReady";
let map;
function init() {
const mapElement = document.getElementById('map');
map = new google.maps.Map(mapElement);
window.parent.postMessage(EVENT_READY, "*");
}
//... rest of the code
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=init">
</script>
</body>
</html>
Then on my wix Page I use the following code to send information to display dynamic point
$w.onReady(async function () {
const officesPromise = // retrieve offices locations from DB;
if(wixWindow.rendering.env === "backend") return;
$htmlMap = $w("#htmlMap");
mapPromise = new Promise((resolve, reject) => {
$htmlMap.onMessage(event => {
if(event.data.name === "mapReady") {
console.log("resolving map") // this execute twice in the editor?!
resolve();
}
});
officesPromise.then(offices => addOfficesToMap(offices)).catch(console.error);
});
Issues are:
-
Code executes twice (sometimes more) in preview. I don’t understand the reason but this triggers the additions of items when the HTML component is not ready (loading a second time?).
-
Code does not execute in production because the HTML component is ready before the page can load (therefore no listener)
Has anyone faced the same issue? What was your solution?
The only workaround I can think off is to have an interval messaging the HTML component every 100ms until it emits a message “mapReady” but I don’t like to use interval when promise could be use
I more carefully read the example @Yisrael uses another technique in his example
I noticed a small issue in the example
<body>
<div id="map" style="height: 100%; width: 100%;" />
<script type="text/javascript">
//... other code
let locations = null;
function init() {
if(locations === null) { // if no locations, let page know
window.parent.postMessage("hello", "*");
}
window.onmessage = (event) => {
if (event.data) {
locations = event.data.markers;
let infowindow = new google.maps.InfoWindow();
let map = new google.maps.Map(document.getElementById('map'));
//... rest of the code
}
}
}
</script>
//... rest of script
</body>
the issue is: locations will always be null since the listener is defined inside the init function.
The correct code should be
<body>
<div id="map" style="height: 100%; width: 100%;" />
<script type="text/javascript">
let locations = null;
let map = null
window.onmessage = (event) => {
if (event.data) {
locations = event.data.markers;
addLocation(locations);
}
}
function init() {
let infowindow = new google.maps.InfoWindow();
map = new google.maps.Map(document.getElementById('map'));
//... rest of map init
if(locations === null) { // if no locations, let page know
window.parent.postMessage("hello", "*");
} else addLocation(locations)
}
function addLocation(locationsToAdd) {
if(!map) return
var markers = locations.map(function (location) {
let marker = new google.maps.Marker({
position: location.position,
map: map
})
//... rest of marker init
var markerCluster = new MarkerClusterer(map, markers);
return marker;
});
}
</script>
//... rest of script
</body>
But this solves the problem of the initialization
Yes, I had that problem a long time ago (component ready before page is ready, your problem 2). My solution: throw it twice. First time, don´t even wait for a ready_response from component (in case it loaded before page), just throw the data at the component. If it´s ready, it will work, if not it will be ignored. Second time (page loaded before component) as you already did, wait for a “ready” and throw it (again). It´s better than a timeout, I think, don´t like that either.