Just Chris

@jsborked

Let’s make Reusable Web Components: Google Maps

A great struggle in moving web dev forward, is the lack of a complete consensus on web components.

This is evidenced by the fact there are frameworks all over, each with their overhead and own ideas, and some common UI components are still developed from scratch.

This is to be expected though, since Frameworks force you into their world, so while a React or Vue component is reusable, you must commit to that framework. Some of us have issues with commitment.

There’s shrinks for that, but they often don’t fix anything.

There is the Web Components standard, born out of the fact that jamming together HTML/JS/CSS is conceptually quite unpleasant.

For instance, we can put Google Maps on our page, as their tutorial instructs.

<div id=”map”></div>
<script>
function initMap() {
// Create a map object and specify the DOM element for display.
var map = new google.maps.Map(document.getElementById(‘map’), {
center: {lat: -34.397, lng: 150.644}, zoom: 8
});
}
</script>
<script src=”https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>

But, is exactly the point. It’s on our page, not in our app.

Of course its a problem of needing the script to load before we use the google.maps object. The script tag should be after the HTML. It’s not nice.

In a world where we probably need many components on the page, we’re stuck with initMap as our entry point. We’re probably loading other scripts, etc.. Gets messy.

So, let’s try a different approach. We have to load JS dynamically.

var e = document.createElement(‘script’)
e.src = s;
document.body.appendChild(e)

For simplicity, we’ll go ahead and call this function load.

const load = async (s) => {
return new Promise(async r=>{
for(i=0;i<document.scripts.length;i++)
if(document.scripts[i].src == s) return r()
let e = document.createElement('script')
e.src = s
document.body.appendChild(e)
e.onload = r
})
}

This promise gets fulfilled when the script has loaded. It also checks first, to see if the script has already been loaded.

So, now we may make our reusable component, a gmap function:

const gmap = async (e) => {
return new Promise(async r=>{
e.style.height = e.style.width = e.getAttribute('size')
let latlng = e.getAttribute('latlng').split(',')
await load('http://maps.googleapis.com/maps/api/js?v=3')
let map = new google.maps.Map(e, {
center: new self.google.maps.LatLng(latlng[0], latlng[1]),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
r(map)
})
}

This is an async function, which will await the load script to complete. This will ensure everything gets done in order. We may want to have multiple maps on a page.

<div control=gmap size=200px latlng=33.808678,-117.918921></div>
<div control=gmap size=200px latlng=28.3852377,-81.5660627></div>

and feed the Divs to the gmap “component” to create the controls

(async () => {
let controls = document.querySelectorAll(‘div[control=gmap]’)
for (var i = 0; i < controls.length; i++)
await gmap(controls[i])
})()

All dependency-free and in vanilla JS.

The important thing here, there’s very little to remember. 
Div, Function, and language features.

Not sure which model will win out, but it is possible to just use the language without the extras, and fun too.

More by Just Chris

Topics of interest

More Related Stories