Leaflet Map in Lightning component on account page

2k views Asked by At

I'm new to lightning components and I am trying to develop a leaflet map component that will display on the account page. When I first click on an account record I get "Map already initialized" error. If I refresh the page it loads fine. Then if I click out and back into the same account it works. But clicking on another account fires the same error as before. I've tried adding the map container dynamically and I've tried removing the map container. Any suggestions would be great.

Thanks!

Here is the code for all the pieces

controller.js

({
jsLoaded: function(component, event, helper) {
    var recType = component.get("v.branch.fields.RecordTypeId.value");
    var action = component.get("c.getBranch");
    action.setParams({'recID' : component.get("v.recordId"),
                      'recordTypeID' : recType});
    action.setCallback(this,function(response){
      var state = response.getState();
      if(state === "SUCCESS"){
        var branch = response.getReturnValue();
        helper.buildmap(component,branch);
      }  
    });
    $A.enqueueAction(action);
},

//future code goes here
})

helper.js:

({
loadAllClients: function(component,map){
    var action = component.get("c.findAll");
    var recType = component.get("v.branch.fields.RecordTypeId.value");
    action.setParams({'recID' : component.get("v.recordId"),
                      'recordTypeID' : recType});
    action.setCallback(this,function(response){
        var state = response.getState();
        console.log(state);
        var objects = response.getReturnValue();
        if(state === "SUCCESS"){
            if(objects != null){
           for (var i=0; i<objects.length; i++) {
            var singleRec = objects[i];
            var url = '/' + singleRec.recID;
            var popup = '<a href=' + url + '>' +singleRec.Name+'</a>';
            var latLng = [singleRec.Latitude, singleRec.Longitude];
            L.marker(latLng, {account: singleRec}).addTo(map)
            .bindPopup(popup);
           }
          }
        }
    });
    $A.enqueueAction(action); 
},
 buildmap: function (component,branch){
    var lat = branch.BillingLatitude;
    var long = branch.BillingLongitude;
    var map = new L.map('map');
    map.setView(new L.LatLng(lat,long), 8);
    setTimeout(function() {
        L.AwesomeMarkers.Icon.prototype.options.prefix = 'fa';
        var redMarker = L.AwesomeMarkers.icon({icon : 'home',markerColor: 'red'});
        var radius = 80467.2;
        L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
        {
           attribution: 'Tiles © Esri'
        }).addTo(map);
        L.marker([lat,long],{icon:redMarker}).addTo(map)
        .bindPopup(branch.Name);
        L.circle([lat,long], radius).addTo(map);
    });
    this.loadAllClients(component,map);     
},//future code goes here
})

Component:

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global" controller="AccountController">
<aura:attribute name="branch" type="account"/>
<aura:attribute name="recordId" type="Id" />
<aura:attribute name="accounts" type="object[]" />

<ltng:require styles="/resource/leaflet/leaflet.css" />  
<ltng:require styles="/resource/fontawesome/font-awesome-4.7.0/css/font-awesome.min.css"/>
<ltng:require styles="http://code.ionicframework.com/ionicons/1.5.2/css/ionicons.min.css"/>
<ltng:require styles="/resource/leaflet/leaflet.awesome-markers.css"/>
<ltng:require scripts="/resource/leaflet/leaflet.js,/resource/leaflet/leaflet.awesome-markers.js" 
         afterScriptsLoaded="{!c.jsLoaded}" />

<force:recordData aura:id="branchservice"
              recordId="{!v.recordId}"
              targetRecord="{!v.branch}"
              fields="Name,RecordTypeId" />

    <div id="map"></div>
</aura:component>
2

There are 2 answers

0
Psymn On

So for anyone who comes across this post. With some trial and error I found my solution. I utilized some other answers I had seen about removing the map. However since the component.get("v.map") was not working, I needed to use document.getElementById('map') which found the map and allowed me to remove it and then re-initialize it. The new code for the buildmap function is:

buildmap: function (component,branch){
    var lat = branch.BillingLatitude;
    var long = branch.BillingLongitude;
    var map;
     try{
         map = new L.map('map');
     }catch(err){
        console.log(err);
         map = document.getElementById('map');
         map.remove();
         map = new L.map('map')
     }
         map.setView(new L.LatLng(lat,long), 8);
        setTimeout(function() {
            L.AwesomeMarkers.Icon.prototype.options.prefix = 'fa';
            var redMarker = L.AwesomeMarkers.icon({icon : 'home',markerColor: 'red'});
            var radius = 80467.2;
            L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
            {
            attribution: 'Tiles © Esri'
            }).addTo(map);
            L.marker([lat,long],{icon:redMarker}).addTo(map)
            .bindPopup(branch.Name);
            L.circle([lat,long], radius).addTo(map);
        });
        this.loadAllClients(component,map);     

}
1
D. Cruz On

In my opinion is not a good practice to manipulate the DOM directly when working with lightning components. Of course there are some cases where there is no other way to do it, but this is not the case. Following there is an other solution that use an aura attribute for storing the map.

Attribute:

<aura:attribute name="map" type="Object" access="private"/>

Then I recommend you to add call the following code when the component initializes and after the scripts are initialized:

var map = component.get("v.map");

//Check if initialized
if(this.isNull(map)){
    map = new L.map('map');
    component.set("v.map", map);
}

To invoke a method on component initialization:

<aura:handler name="init" value="{!this}" action="{!c.doInit}" access="private" />

To invoke a method on scripts initialization:

<ltng:require scripts="/resource/leaflet/leaflet.js"
             afterScriptsLoaded="{!c.jsLoaded}" />

Also I recommend you to check out this trailhead project: https://trailhead.salesforce.com/en/projects/account-geolocation-app/steps/lc-app-06