How NOT to add Google Maps to a Lightning Component!

This post about how you can create a Google Maps like Map component inside a Lightning component but without using Google Maps.

Wait You Said Google Maps in Lightning?

It would be awesome if you could embed Google Maps in a Lightning component and there are lots of posts that show how to do it, but if you look at them you’ll see they all use an iframe to embed it. The reason they need to use an iframe is because the Google Maps JS api has to be used from their servers. There’s no way in Lightning to use a JS library like that.

Now you maybe thinking… “well why not download it and upload it as a Static Resource?”. You can, but it’s not so simple because of the way Google dynamically generates/downloads the files.

What options are there then?

Well thankfully Google Maps isn’t the ONLY map solution available. Instead you can use a library called Leaflet.js.

Leaflet
Leaflet

Why use Leaflet?

  • It’s open source
  • It’s mobile friendly
  • It’s tiny (37kb)
  • And, as we’ll see, its super simple to use!

Getting started

Firstly we’ll need to upload Leaflet as a static resource. The latest builds are available from their download page here:

http://leafletjs.com/download.html

In the Setup screen of your org, go to Static Resources and upload the latest stable version naming it leafletjs like so:

Static Resource Leaflet Js

Now open up the Developer Console and we’ll create a New Lightning Component called LeafletMap :

Create New Lightning Component

Inside of the Component xml file we’ll need to do 2 things:

  • load leaflet.js using <ltng:require />
  • add <div aura:id="mapid"></div> to render our Map into

So our in component file add the following:

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >

    <ltng:require scripts="{!$Resource.leafletjs + '/leaflet.js'}"
                  afterScriptsLoaded="{!c.afterLeafletLoaded}" />

     <div aura:id="mapid"></div>

</aura:component>

Adding some style

Now there’s one thing we can’t do with Lightning… load CSS files 🙁 So we’ll have to copy the entire CSS into our style file for the component. However we can’t just blindly copy and paste the contents. Every CSS rule has to be prefixed with .THIS so it’s scoped to our component only!

So here’s my hack for this 🙂 Use SASS! Here’s a quick video I recorded to show what to do:

SassMeister - import css from an external library into your Lightning component
SassMeister – import css from an external library into your Lightning component
  1. Open https://www.sassmeister.com/
  2. Add .THIS { } into the editor
  3. Open the CSS file from whatever library you need, in our case leaflet.css
  4. Copy paste the contents from the CSS file in between the { } in SassMeister
  5. Finally copy the generated CSS from SassMeister into your component’s Style file 🙂

Now to complete the styles for our component I need to give our Map <div/> element a height, so I’ll set it to be 180px. This needs to be in the style file of your component and I suggest adding it at the top, so all the copy/pasted styles are at the bottom:

.THIS {
   height: 320px;
   width: 100%;
}

/* Leaflet.css */
...

Let’s render a Map!

Now we have all the core bits and pieces setup let’s render our first Map!

Open the Controller file and we need to first add a handler to execute after leaflet.js has loaded called afterLeafletLoaded. In that handler we’re going to set up our Map to point somewhere- I’ve chosen Salesforce HQ which also happens to be Vlocity’s San Francisco office!

({
    afterLeafletLoaded : function(component, event, helper) {
        var mapDiv = component.find('mapid').getElement();
        var mymap = L.map(mapDiv).setView([37.790503, -122.397215], 13);
    }
})

Now let’s add it to a page. I’m going to add it to an Account Page:

  1. Go to an Account in your org and click the icon in the top right. Then choose Edit Page.
  2. In the Lightning App Builder find your LeafletMap component in the palette on the left and drag it into place on your page.
  3. Hit (and if you haven’t overridden the default Account Page click the ). Then click at the top of the screen.
Add Leaflet component to your Account Page
Add Leaflet component to your Account Page

Now you should see an empty map. That’s because there’s one more step we need to do before we can render anything… Adding tiles!

Adding Tiles to your Map

Tiles are the images of the Map you see. There are satellite tiles, street tiles and others. They need to be download from a service that supplies them. Leaflet.js works independently of different tile services. For example you can get the map tiles from:

Some of these require registration to use and some of them also require extra JS libraries… including Google Maps 🙁 But some of them don’t!

I’m going to go with MapBox since it’s free for 50,000 views, though it does require registration for an api token, so take a second out to register and claim your API token: https://www.mapbox.com/signup/

Mapbox Sign up
Mapbox Sign up

Now with your token we should be able to finish up our controller. So let’s go back to our Controller in the Developer Console and update it with the following:

({
    afterLeafletLoaded : function(component, event, helper) {
        var mapDiv = component.find('mapid').getElement();
        var mymap = L.map(mapDiv).setView([37.790503, -122.397215], 13);
        L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
            attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>',
            maxZoom: 18,
            id: 'mapbox.streets',
            accessToken: 'your.mapbox.access.token'
        }).addTo(mymap);
    }
})

Replace ‘your.mapbox.access.token’ with your access token.

Save that and go back to your Account page and refresh (a couple of times until you see the Attribution text). You should now see:

Still No Map Data!
Still No Map Data!

Damn it Goldspink! Still no Map!?! What the hell!

Well this is where people start to hate Lightning because things don’t work out of the box how they’re supposed to. But that’s also why I’m writing this to save you the headache’s and hopefully teach you the techniques to diagnose these issues for yourself!

So let’s crack open the Chrome Developer Tools and take a poke around.

Now I poked around a little and expanded the DOM structure to find out what was in there. Turns out you can see the Map tiles being downloaded! AWESOME!

Chrome Developer Tools for Leaflet in Lightning

So why don’t they appear? Well if you look in my video above again you’ll see the dimensions of the images. I’ve snapped a screenshot below to highlight this. Check out the reported width along with its natural width. It’s 0px!

Why is my width 0px?
Why is my width 0px?

And the reason for this is because of this rule on the right hand side max-width:100%. This is something that Lightning helpfully sets for us! So let’s override it in our CSS by resetting it to its initial value:

.THIS img {
   max-width: initial;
}

Save that and refresh our Account screen:

So Close Now!
So Close Now!

Wow! We’re so close now, the Map is appearing but it’s overflowing everywhere!!! Let’s add one more rule to our CSS to not allow anything to overflow outside of our component. Here’s the complete CSS (minus the Leaflet.js css rules) for reference:

.THIS {
   height: 320px;
   width: 100%;
   overflow: hidden;
}

.THIS img {
   max-width: initial;
}

/* Leaflet.css */

Save and refresh….

Finally A Map!
Finally A Map!

Finally we have a MAP!!!!

Drag it around, **Zoom* in! It works! No Iframe’s were involved. This is Locker Service compatible and can be shipped to your users!

What next!?

We could go further and set it up to use the Billing Address or Longitude, Latitude from an Object. But I’ll leave those for another follow-up post.

I’d highly recommend checking out the Leaflet.js tutorials for how to add Markers, change the Tiles and more!

Leave a Reply