To render Google Map in Salesforce LWC we have a couple of options, let’s discuss those first:
- MAP Base Component in LWC
Out of the Box in Salesforce, no need for additional Google MAP API; Only Plots the traverse points in the map, no route information
https://developer.salesforce.com/docs/component-library/bundle/lightning-map/documentation
- Google MAP API implementation ONLY in LWC
Technically, this is not possible at this moment due to Security with Lightning Locker Services which prevents to run a script from a different domain/CDN
- Google MAP API implementation with LWC + VF
The only possible design currently is placing a VF inside an LWC to render the Google Map and show the route. All logic is inside the LWC- VF is only for rendering
- Buy Field Service Lighting or SALESFORCE MAPS licenses
We will only discuss the Google MAP API implementation with LWC + VF solution in this block. This is the only possible solution that needs customization in LWC, VF and APEX.
Prerequisite: Google MAP API License key for commercial use
The solution and Implementation steps are:
- A Quick Action to add a Lightning LWC Button to a page layout to access google form.
- Clicking on the button will open an LWC Component, LWC component will get the current record id and will call the apex controller to get the data.
- Get all required field information from the Apex controller class.
- Pass the Information info to the VF page to render the Google Map.
- Google Map is dynamically rendered based on the JSON input received from the LWC component.
- It has one iframe to host the VF page that will render the Google Map by using the google map javascript library.
- This solution has one unique challenge – to communicate data from the LWC component to the VF page and from the VF page to send the data to the Google Javascript library. All event sequences need to be followed properly.
- LWC component will read data from the salesforce object and send the location data to google Map.
- It plots all the locations to Map with the route.
- The right-hand side panel shows the distance between each point. All Data is dynamically populated from google maps.
VF code to render Google MAP
<apex:page showHeader="false" >
<apex:includeLightning />
<div id="rcontainer">
</div>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps API v3 Directions Example</title>
<script type="text/javascript" src="//maps.google.com/maps/api/js?key=GOOGLE API KEY"></script>
</head>
<body style="font-family: Arial; font-size: 12px;">
<script >
/*** EventListener to GET response from LWC ***/
/*** EventListener to GET response from LWC ***/
window.addEventListener("message", function(event) {
var directionsService = new google.maps.DirectionsService();
var directionsDisplay = new google.maps.DirectionsRenderer();
var map = new google.maps.Map(document.getElementById('map'), {
zoom:7,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: true,
disableDefaultUI: false,
});
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById('panel'));
console.log('vf page data'+event.data);
var request = {
origin: '',
destination: '',
waypoints: [],
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
console.log('request1'+request);
let counter=0;
for (let i = 0; i < event.data.length; i++) {
console.log('Check_In_Location__Latitude__s'+event.data[i].Check_In_Location__Latitude__s);
console.log('Check_In_Location__Longitude__s'+event.data[i].Check_In_Location__Longitude__s);
if(i==0){
var StartLocationA = new google.maps.LatLng(event.data[i].Check_In_Location__Latitude__s, event.data[0].Check_In_Location__Longitude__s);
request.origin=StartLocationA;
console.log('request2'+request);
}
else if(i==event.data.length-1){
var EndLocationB = new google.maps.LatLng(event.data[i].Check_In_Location__Latitude__s, event.data[0].Check_In_Location__Longitude__s);
request.destination=EndLocationB;
console.log('request3'+request);
}
else{
var StopOver1 = new google.maps.LatLng(event.data[i].Check_In_Location__Latitude__s, event.data[0].Check_In_Location__Longitude__s);
var waypointjson={
location: StopOver1,
stopover: true,
};
request.waypoints[counter]=waypointjson;
counter++;
console.log('request4'+request);
}
}
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
hideDetail();
}
});
});
//This is a POC code, Loading div needs to implemented for actual implementation
function hideDetail() {
setTimeout(
function() {
for (let i = 0; i < document.getElementsByClassName('adp-directions').length; i++) {
document.getElementsByClassName('adp-directions')[i].style.display='None';
}
}, 100);
setTimeout(
function() {
for (let i = 0; i < document.getElementsByClassName('adp-directions').length; i++) {
document.getElementsByClassName('adp-directions')[i].style.display='None';
}
}, 500);
setTimeout(
function() {
for (let i = 0; i < document.getElementsByClassName('adp-directions').length; i++) {
document.getElementsByClassName('adp-directions')[i].style.display='None';
}
}, 1000);
}
const contentString = ;
const infowindow = new google.maps.InfoWindow({
content: contentString,
});
</script>
</body>
</apex:page>
Hi Sudipta, this is very helpful, however I think you have an incomplete code for this line const contentString = ;
Thanks John. Right, I may update the block sometime.
Have you published a completed code base for this solution? If so, we would be interested in it.