Since I was checking Force.com documentation in order to pass Platform Developer Transition exam I had the opportunity to learn more about some topics that I don’t usually use on my daily job.
One of those are Maps, and I’m not talking about Collections (Lists, Sets and Maps). I would like to talk about locations on this post.
Till some releases ago, the only way to add a location into your visualforce page, was via JavaScript. Let’s see an example. (Bellow use case is based on this Salesforce Trailhead)
I’m working on a healthcare app, and we would like to show a map on every doctor’s record, so that, the customer would be able to find the location easily.
In order to get it, I will create a visualforce page with the map and include it in the standard layout.
JavaScript Map Version
First thing is to add the listener:
google.maps.event.addDomListener(window, 'load', initialize);
that call an initialize function
var map; function initialize() { var mapOptions = { center: new google.maps.LatLng(43.2616465, -2.9393368), zoom: 15 }; map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions); loadDoctor(); }
Where we define map properties, like latitude, longitude and zoom.
But this function call another, loadDoctor
function loadDoctor() { Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.DoctorsController.findTheDoctor}', '{!Id}', function(result, event) { if (event.status) { var name = result.SurgeryName__c; var lat = result.Location__Latitude__s; var lng = result.Location__Longitude__s; addMarker(name, lat, lng); } else { alert(event.message); } }, {escape: true} ); }
Above function uses a RemoteAction method, so we know that a Controller is included on this page. This communication with the controller just returns information about the Doctor’s records like his place name and the Location. Maybe you are wondering what below line means.
Location__Latitude__s and Location__Longitude__s
Location is a Geolocation field, and, in the end, it is a Compound field, so in order to see the information, we need to decouple into its “elements”, Latitude and Longitude.
Finally, on above method, we have a call to another function that will add the marker on the google map
function addMarker(name, lat, lng) { var marker = new google.maps.Marker ({ position: new google.maps.LatLng(lat, lng), map: map, title: name }); }
With this code, the result is this one:
Apex Map Version
Is there a different way to get the same result? Yes !! Since Spring ’15 we have a new tag available on visualforce pages: apex:map and with this piece of code:
<apex:map width="100%" height="100%" mapType="roadmap" zoomLevel="15" center="43.2616465, -2.9393368"> <apex:mapMarker title="{!Doctor__c.SurgeryName__c}" position="{!Doctor__c.Location__Latitude__s}, {!Doctor__c.Location__Longitude__s}"> </apex:mapMarker> </apex:map>
we can get exactly what we had on the javascript code as we can see on below image that compares both maps. Left side uses apex:maps whereas on the right side the map is created with javascripts.
More about apex:map
Attributes
- Height and Width could be defined as pixels or percentage
- Center could be used with Location data, Address and also with JSON format. From my point of view, it would be easier to create a method in the controller with the data
public Map<String,Double> getDoctorsCenter() { Map<String,Double> mapCenter = new Map<String,Double> { 'latitude' => 43.2616465, 'longitude' => -2.9393368 }; return mapCenter; }
and use the method in the visualforce page.
<apex:map width="100%" height="100%" mapType="roadmap" zoomLevel="15" center="{!doctorsCenter}">
Above example set geolocation with hardcode but if we have this information on data base, the code could be safer and don’t fail if there is any change on this data.
MapMarker
- Similar as before, position attribute could be defined based on Address, JSON or using the Geolocation field. On above example, we directly used the compound sub-fields, but it would be also fine to get this information in the controller, create the map and call the method in the page as we show on center attribute.
- If your idea is to show all doctors around your city, instead of adding an mapMarker per record, remember to use apex:repeat tag
<apex:map width="100%" height="100%" mapType="roadmap" zoomLevel="15" center="43.2616465, -2.9393368"> <apex:repeat value="{!locations}" var="pos"> <apex:mapMarker position="{!pos}"/> </apex:repeat> </apex:map>
- Another mapMarker attribute is icon that help us to show a different image than the red bubble that google offer to us. A good way to show it is using a static resource and make a call to it.
<apex:mapMarker title="{!Doctor__c.SurgeryName__c}" position="{!Doctor__c.Location__Latitude__s}, {!Doctor__c.Location__Longitude__s}" icon="{!URLFOR($Resource.MapMarkers, 'myIcon.png')"> </apex:mapMarker>
MapInfoWindows
- This is another tag to show even more information. So for instance, below code show the doctor’s name and phone if we click on the marker icon
<apex:map width="100%" height="100%" mapType="roadmap" zoomLevel="15" center="43.2616465, -2.9393368"> <apex:mapMarker title="{!Doctor__c.SurgeryName__c}" position="{!Doctor__c.Location__Latitude__s}, {!Doctor__c.Location__Longitude__s}"> <apex:mapInfoWindow > <apex:outputPanel layout="block" style="font-weight: bold;"> <apex:outputText >{!Doctor__c.SurgeryName__c} </apex:outputText> </apex:outputPanel> <apex:outputPanel> <apex:outputText >{!Doctor__c.Phone__c} </apex:outputText> </apex:outputPanel> </apex:mapInfoWindow> </apex:mapMarker> </apex:map>
JavaScript vs apex:map
- Unfortunately, we cannot create a page with map tag in a Developer Edition, however JavaScript is available on Developer editions. But if you want to give it a try, create your own Trial edition.
- As JavaScript is executed on the client side, the map is loaded quicker than the one that use apex:map.
- On the other side, Salesforce takes care of any apex tag so any change on code versions, or browser, etc. that could make the code fail, Salesforce will deal with it.
- JavaScripts needs a method in the controller in order to get Location information dynamically and if we want to show the map inside a Standard layout, we must define the RemoteAction method as global that makes you define the class as global as well. This is not important till you add the code inside a package. As a reminder, global classes are available to everybody that install your package and maybe this is not your desire.
- RemoteAction is not able to get parameters from url. So I cannot do something like
Id currentDoctorId = controller.getId()
and then use it on the RemoteAction method. However, in our case, we can use directly the Id of the record on the visualforce page as a workaround:
Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.DoctorsController.findTheDoctor}', '{!Id}', function(result, event){....});
Resources
- Apex:map Salesforce documentation
- Github Repository with code used on above examples
Thanks for the entry, Agustina, pretty interesting 🙂
I have one doubt, though:
I usually use dev orgs to create and publish packages. If the map component is not supported in dev orgs, does that mean that I cannot package these kind of components unless I use other org editions?
To be honest, never thought about this question until now. Anyway, it would be a shame not to be able to take advantage of this in dev orgs.
LikeLiked by 3 people
Thanks jmarcas!! It’s a pity, but as far as I know, we cannot package this functionality. The only thing is to develop on a Sandbox org and Deploy into production via Change Set, for instance.
LikeLiked by 3 people
Hola Agustina, tienes un blog estupendo aquí.
Estoy revisando tu código, la verdad es que soy nuevo en la programación en general (tengo al rededor de 8 meses estudiando java) y recientemente estoy comenzando a entrar en el mundo de SalesForce.
Tengo un ejercicio en el cual debo añadir una funcionalidad de geolocalización a las ventanas de Accounts predeterminadas. Me pareció que tu código me serviría como base para lograrlo, pero me está costando descifrar cómo hacerle los cambios correspondientes.
Lo que necesito es que desde la pestaña de Accounts aparezca el mapa, y que el mapa muestre en el las direcciones Billing, Shiping y Fiscal.
No entiendo bien la forma en la que usas la latitud y longitud. ¿Podrías ayudarme a entender cómo leer las direcciones de una cuenta, (Account.BillingAdress, etc) para que esta funcione como los parámetros en los métodos de la clase?
Agradezco de antemano cualquier ayuda que puedas darme. Saludos.
LikeLike
Hola!! Me alegra que hayas llegado a mi post y te haya parecido interesante. Pero desde esta entrada ha llovido mucho en el mundo de Salesforce. Aquí hablo de cómo añadir un mapa usando Js vs visualforce (apex:map) pero si está aprendiendo te recomiendo ir directamente a Lightning Web Components porque es el futuro. Desde mi punto de vista, LWC es el futuro y llegará un momento en que visualforce se quede obsoleto.
Mi recomendación es echarle un ojo a esta página: https://developer.salesforce.com/docs/component-library/bundle/lightning:map/example dónde hay ejemplos que puedes seguir. Yo también escribí un post, a ver si te puede ayudar también: https://agarciaodeian.com/2019/01/
Una vez que tengas el LWC creado, lo puedes añadir a la página de Account. Si abres un registro de Account, debajo de la rueda de Setup, está la opción de Edit Page, que abre el App Builder y con un drag and drop puedes añadir tu custom LWC.
Si tienes más problemas, no dudes en avisar. Te recomiendo también el foro de developer donde puedes preguntar lo que necesites: https://developer.salesforce.com/forums?dc=Apex_Code_Development#!/feedtype=RECENT&criteria=ALLQUESTIONS&
LikeLiked by 2 people
Pingback: Adding Maps to Visualforce using Javascript or Apex – Salesforce.Study Blog
Pingback: New Lightning Component for Maps – Agustina odeian
¡Te agradezco mucho la respuesta, Agustina! Se trataba de una asignación que requería el uso de Visualforce, pero de todas formas le echaré un vistazo al contenido que me anexaste para aprender sobre ello, posiblemente me sea útil en futuros proyectos personales.
Saludos, y de nuevo gracias por tu amabilidad. 😀
LikeLiked by 1 person
En ese caso échale un ojo a esto: https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_compref_map.htm Creo que usan los campos que necesitas
LikeLike