<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Apex Archives - Michael Soriano</title>
	<atom:link href="https://michaelsoriano.com/tag/apex/feed/" rel="self" type="application/rss+xml" />
	<link>https://michaelsoriano.com/tag/apex/</link>
	<description>I turn code into captivating user experiences for the web</description>
	<lastBuildDate>Wed, 02 Feb 2022 18:21:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.4</generator>
	<item>
		<title>Let&#8217;s build a Map Application using Leaflet and Lightning Components</title>
		<link>https://michaelsoriano.com/lets-build-map-application-using-leaflet-and-lightning-components/</link>
					<comments>https://michaelsoriano.com/lets-build-map-application-using-leaflet-and-lightning-components/#comments</comments>
		
		<dc:creator><![CDATA[Michael Soriano]]></dc:creator>
		<pubDate>Sat, 15 Sep 2018 23:57:59 +0000</pubDate>
				<category><![CDATA[Salesforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[Aura]]></category>
		<category><![CDATA[lightning]]></category>
		<guid isPermaLink="false">http://michaelsoriano.com/?p=6575</guid>

					<description><![CDATA[<p>Salesforce&#8217;s Lightning component system is quite a robust framework where you can build full pledged single-page applications in a heartbeat. I especially like it&#8217;s built-in SLDS (styles), so all you have to really think about is the logic of your application. In this walk trough, we&#8217;re building a real life map application with Lightning. We&#8217;re [&#8230;]</p>
<p>The post <a href="https://michaelsoriano.com/lets-build-map-application-using-leaflet-and-lightning-components/">Let&#8217;s build a Map Application using Leaflet and Lightning Components</a> appeared first on <a href="https://michaelsoriano.com">Michael Soriano</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Salesforce&#8217;s Lightning component system is quite a robust framework where you can build full pledged single-page applications in a heartbeat. I especially like it&#8217;s built-in SLDS (styles), so all you have to really think about is the logic of your application. In this walk trough, we&#8217;re building a real life map application with Lightning. </p>



<p>We&#8217;re using Google Places for our city lookup, as well as Leaflet for our map and finally Chatter for our chat component.<br></p>



<p>The goal is to have pins on the map &#8211; which is on the right side of our page. These pins are also shown in a list format on the left hand side of the page. Once a pin is clicked, the list on the left hand side is replaced by the pin details, along with the ability to chat about that specific pin directly below. When the &#8220;Close&#8221; button is clicked, the list re-appears, as well as the map zooms to its original view.</p>



<p><br>The same behavior is achieved when the pin on the map is clicked.</p>



<p><br><img fetchpriority="high" decoding="async" width="930" height="465" class="alignnone size-full wp-image-6663" style="border: 1px solid #ededed;" src="https://michaelsoriano.com/wp-content/uploads/2018/07/map-application3.gif" alt="Salesforce Map"><br></p>



<p>We also have a custom form, that allows us to add a pin right in the same page. The form has an auto suggest field for the cities.<br></p>



<p>Note that this is going to be a high level tutorial. You should be well versed in Lightning Components and JavaScript to follow along. A working knowledge of Salesforce is also needed.<br></p>



<p>Read to get started? Let&#8217;s begin.</p>



<h3 class="wp-block-heading" id="setting-things-up">Setting things Up</h3>



<p>Before we can actually start coding, let&#8217;s back up and think about what we need. We will need a map software.&nbsp; We need to store the locations in a custom object in Salesforce. And we need an Apex class to fetch these records for us. Seems simple enough? Let&#8217;s continue.</p>



<p><br><strong>Static Resources</strong><br></p>



<p>We&#8217;re using Leaflet &#8211; an open source map software which allows us to build a map on the page. All we need to do is pass it an array of locations (Longitude + Latitude) and Leaflet will populate magically. We also need LeafletMarkerCluster, an add-on to Leaflet, which allows us to bundle our pins together. This prevents the ugly grouping of many pins together.<br></p>



<p>So add these two zip files as static resources to Salesforce:</p>



<ul class="wp-block-list"><li><a href="https://michaelsoriano.com/wp-content/uploads/2019/05/leaflet.zip">Leaflet</a></li><li><a href="https://michaelsoriano.com/wp-content/uploads/2019/05/leafletMarkerCluster.zip">LeafletMarkerCluster</a></li></ul>



<p><img decoding="async" width="579" height="500" class="alignnone size-full wp-image-6655" src="https://michaelsoriano.com/wp-content/uploads/2018/07/static-resources.png" alt="Static Resources" srcset="https://michaelsoriano.com/wp-content/uploads/2018/07/static-resources.png 579w, https://michaelsoriano.com/wp-content/uploads/2018/07/static-resources-300x259.png 300w" sizes="(max-width: 579px) 100vw, 579px" /><br></p>



<p><strong>Custom Object</strong><br></p>



<p>Each pin on the map is a record from a Custom Object in Salesforce. Let&#8217;s call ours &#8220;Pin&#8221;. Go ahead and build that custom object with the fields shown below.</p>



<p><br><img decoding="async" width="803" height="644" class="alignnone size-full wp-image-6671" src="https://michaelsoriano.com/wp-content/uploads/2018/07/custom-obj-pin.png" alt="custom object" srcset="https://michaelsoriano.com/wp-content/uploads/2018/07/custom-obj-pin.png 803w, https://michaelsoriano.com/wp-content/uploads/2018/07/custom-obj-pin-300x241.png 300w, https://michaelsoriano.com/wp-content/uploads/2018/07/custom-obj-pin-768x616.png 768w" sizes="(max-width: 803px) 100vw, 803px" /><br>You also might need to enter a few records. Since we&#8217;re dealing with coordinates, you will need to Google maps and grab this information. Don&#8217;t worry &#8211; this is just for the prototype. We are building our own custom form &#8211; that will take care of all this information for us.<br></p>



<p><strong>The Apex Class</strong><br></p>



<p>An Apex Class can behave sort of like a hash table that can hold our functions. This is how we&#8217;re going to interact with data from our Lightning components. Go ahead and create a class called &#8220;Pin&#8221;. Let&#8217;s add a method in there that grabs all of our Pins as well:</p>



<pre class="wp-block-code"><code lang="java" class="language-java">public with sharing class Pin {
    public static List&lt;Pin__c&gt; getPins(){
        String sql = 'select Name, City__c, Lat__c, Long__c, Content__c, 
        Country__c, Region__c, Continent__c from Pin__c ORDER BY CreatedDate  
        DESC';
        List&lt;Pin__c&gt; PinList = Database.query(sql);
        return PinList;
    }
}</code></pre>



<p>Above is a class that has one static method &#8220;<strong>getPins</strong>&#8220;. This means that we don&#8217;t have to instantiate the class to use the method. The <strong>@auraEnabled&nbsp;</strong>declaration also make it callable directly from our Lightning components.</p>



<p><br><strong>Component</strong><br></p>



<p>Once that&#8217;s done, go ahead and create a Lightning component &#8211; let&#8217;s call ours &#8220;<strong>Pin</strong>&#8220;. Open <strong>pin.cmp</strong> and add the code below:</p>



<pre class="wp-block-code"><code lang="markup" class="language-markup">
&lt;aura:component implements="flexipage:availableForAllPageTypes"  access="global" controller="Pin"&gt;
    &lt;ltng:require styles="{!$Resource.leaflet + '/leaflet.css'}" /&gt;
    &lt;ltng:require styles="{!$Resource.leafletMarkerCluster + '/leafletMarerClusterDefault.css'}" /&gt;
    &lt;ltng:require styles="{!$Resource.leafletMarkerCluster + '/leafletMarerCluster.css'}" /&gt;
    &lt;ltng:require scripts="{!join(',',$Resource.leaflet + '/leaflet.js', $Resource.leafletMarkerCluster + '/leafletMarerCluster.js')}" afterScriptsLoaded="{!c.jsLoaded}" /&gt;
&lt;/aura:component&gt;</code></pre>



<p>You will see that all we&#8217;re doing is setting our static resources up in our component file. We&#8217;re also doing&nbsp;<strong>controller=&#8221;Pin&#8221; </strong>in our component declaration. This means that we&#8217;re going to create an Apex class called &#8220;Pin&#8221;. We&#8217;ll get to that later.</p>



<p>Also note we have <strong>afterScriptsLoaded=&#8221;{!c.jsLoaded}&#8221; </strong>in our lightning scripts tag.&nbsp; This is because we want the function <strong>jsLoaded</strong> to run as soon as our scripts are loaded. We don&#8217;t have this function yet &#8211; so loading the page will cause an error. We&#8217;ll get to this function soon.</p>



<p>Let&#8217;s continue building the markup.</p>



<p>Still in our .cmp file, after our scripts and styles &#8211; add our markup below :</p>



<pre class="wp-block-code"><code lang="markup" class="language-markup">&lt;aura:attribute name="pins" type="List" default="[]"/&gt;
&lt;aura:attribute name="singlePin" type="Object" default="{}"/&gt;
&lt;div class="slds-grid"&gt;
        &lt;div class="leftcol-wrap slds-col slds-size_1-of-3"&gt;
            &lt;!--THIS IS THE LEFT HAND COLUMN, WHERE THE LOCATIONS WILL GO--&gt;
            &lt;!--THIS IS ALSO WHERE THE SINGLE LOCATION DETAIL WILL GO--&gt;
        &lt;/div&gt;&lt;!--leftcol-wrap--&gt;
        &lt;div class="map-wrap slds-col slds-size_2-of-3"&gt;
            &lt;div class="map" id="map" aura:id="map"&gt;&lt;/div&gt;
        &lt;/div&gt;&lt;!--end map-wrap--&gt;
&lt;/div&gt; &lt;!--end slds-grid--&gt;</code></pre>



<p>The &#8220;<strong>aura:attribute</strong>&#8221; named &#8220;<strong>pins</strong>&#8221; is simply a holder for the pins we&#8217;re grabbing from the controller. </p>



<p>More on this later.</p>



<p>The&nbsp;&#8220;<strong>aura:attribute</strong>&#8221; named &#8220;<strong>singlePin</strong>&#8221; is also a holder &#8211; for a pin that&#8217;s in focus. Again, more on this later.</p>



<p>This sets up our page. We&#8217;re splitting the page in two, one column is smaller (size_1-of-3) and the other bigger (size_2-of-3). We&#8217;re using built in Salesforce classes, with the prefix &#8220;slds&#8221;, stands for <a href="https://www.lightningdesignsystem.com">Salesforce Lightning Design System</a>. In case you&#8217;re not familiar with it &#8211; it&#8217;s sort of like Bootstrap and React fused into a single framework.</p>



<h3 class="wp-block-heading" id="the-map">The Map</h3>



<p>So we&#8217;re ready to load the page and initialize our Leaflet map. We have our temp data in our custom object, we should have our component &#8211; along with the JavaScript files in place. Let&#8217;s write the&nbsp;<strong>jsLoaded</strong>() function (this is called when our scripts are done loading):</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">jsLoaded: function(component, event, helper) {
	var mapOptions = {
		zoomControl: true,
		zoomAnimation:false, //fixes the error when zooming in...
		markerZoomAnimation:true
	}
        var map = L.map('map', mapOptions)
        map.setView([48.85661400000001, 2.3522219], 2);
        map.scrollWheelZoom.disable();
        helper.map = map;
        L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {attribution: 'Location'}).addTo(map);
        helper.callServer(component,'c.getPins', function(response){
        	component.set('v.pins',response);
        	helper.buildMap(component, event, helper, response);
        },{});
	}</code></pre>



<p>Okay, a lot going on here. We&#8217;re setting some map default options (for more info on Leaflet, see their <a href="https://leafletjs.com/reference-1.3.2.html">documentation</a>).&nbsp;&nbsp;We also have a very important helper function called &#8220;.<strong>callServer()</strong>&#8220;. This function is responsible for making the call to our Apex class, and returns our data. Note that I talk about <strong>callServer</strong> in a previous <a href="https://michaelsoriano.com/call-apex-from-lightning-components/">post</a>. Simply add this function in your <strong>PinHelper.js</strong> file and that should be good to go.<br>Inside our callback we&#8217;re setting the <strong>v.pins</strong>&nbsp;in our .cmp file &#8211; which is an array of objects that came back from our server.</p>



<p>Right after we have a function called .<strong>buildMap()</strong>&nbsp;&#8211; which is a pretty big function and discussed in detail below.</p>



<h3 class="wp-block-heading" id="the-map">The Map</h3>



<p>So our pins our fetched from the server. I don&#8217;t know if you remember, that we are using a Leaflet add-on called <strong>LeafletMarkerCluster</strong> &#8211; which is what does the clustering of the pins &#8211; that are in the same proximity. What this does is &#8211; it creates a nice visual &#8211; the number of pins in a specific grouping, that you can click &#8211; and it will zoom in further to show the pins.</p>



<p>It also has a nice &#8220;spider&#8221; effect &#8211; for the clusters &#8211; which is really cool:</p>



<p><img decoding="async" width="669" height="380" class="alignnone size-full wp-image-6666" style="border: 1px solid #ededed;" src="https://michaelsoriano.com/wp-content/uploads/2018/07/clusters.gif" alt="Cluster"><br>So what we need is a way to group our pins. Notice our custom object earlier &#8211; we have a field called &#8220;Region&#8221;. A region is similar to a &#8220;State&#8221; in the United States. So every pin that share the same region &#8211; will be clustered. Make sure your test data have pins that have the same region, and some that don&#8217;t.</p>



<p>Don&#8217;t worry &#8211; we don&#8217;t have to figure out what region our pins need to be &#8211; remember we&#8217;re building our own form.</p>



<p><strong>The .buildMap() function</strong></p>



<p>Now let&#8217;s dig in to the actual code that adds and groups the pins to the map. Open <strong>PinHelper.js</strong> and add the code below:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">map : {},
mapLayers : {},
markerGroups : [],
markerList : [],
buildMap : function(component, event, helper, response){
        var map = helper.map;
        function onMarkerClick(e){
            //TODO: ADD LOGIC WHEN MARKER IS CLICKED
        }
        function onlyUnique(value, index, self) {
            return self.indexOf(value) === index;
        }
        function popupHtml(pinName){
            return '&lt;strong&gt;' + pinName + '&lt;/strong&gt;';
        }
        var markerGroups = helper.markerGroups;
        for(var y=0; y&lt;markerGroups.length;y++){
            markerGroups[y].clearLayers();
        }
        var groups = [];
        for(var i=0; i&lt;response.length;i++){
           groups.push(response[i].Region__c);
        }
        var uniqueGroups = groups.filter(onlyUnique);
        var markerOpts = {
            draggable : false
        }
        var markerList = [];
        for(var x=0; x&lt;uniqueGroups.length;x++){
            var markers;
            markers = L.markerClusterGroup();
            helper.markerGroups.push(markers);
            for(var i=0; i&lt;response.length;i++){
                if(response[i].Region__c == uniqueGroups[x]){
                    markerOpts.alt = response[i].Id; //marker options
                    markerOpts.icon = L.icon({iconUrl : 'YOURICONIMAGE.png'});
                    var mark = L.marker([response[i].Lat__c,response[i].Long__c],markerOpts).on('click', onMarkerClick);
                    var popup = mark.bindPopup(popupHtml(response[i].Name));
                    popup.on('popupclose',function(){
                        helper.closeSinglePin(component, event, helper);
                    })
                    markers.addLayer(mark).addTo(map);
                    markerList[response[i].Id]=mark;
                }
            }
        }
        helper.markerList = markerList;
    },</code></pre>



<p>First, let&#8217;s set some variables in our helper (lines 1-4). These will act as a container as we pass them around from our controller to helper and vice versa.</p>



<p>Then the actual buildMap() function. We have gone through our pins and determined the unique regions and stuffed them into&nbsp;<strong>helper.markerGroups. </strong>Now starting in line 39, we loop through each of pin and add them to the map &#8211; according to their respective groups (region). Passing along the necessary options for each pin.</p>



<p>Your map should now load with the clusters and the pins.</p>



<h3 class="wp-block-heading" id="the-list-of-pins-left-side">The List of Pins (Left Side)</h3>



<p>On the left side of the application &#8211; we show all of the pins &#8211; sorted by latest entry. Remember that we set our pins as a component attribute in our <strong>jsLoaded() </strong>function? Now all we need to do is loop through this in our component. But first, we have to check if there&#8217;s a&nbsp;<strong>singlePin </strong>&#8211; why you ask? This is to determine if we clicked on a marker on the map OR we clicked on any of the item in our list (which we&#8217;re still building).</p>



<p>We do this by using the&nbsp;<strong>aura:if </strong>directive<strong>.</strong></p>



<p>The code below goes into the left hand section.</p>



<pre class="wp-block-code"><code lang="markup" class="language-markup">&lt;aura:if isTrue="{!v.singlePin.Id}"&gt;
	&lt;div class="singlePin" aura:id="singlePin"&gt;
		&lt;lightning:button class="closeSinglePin" onclick="{!c.closeSinglePin}"&gt;Close&lt;/lightning:button&gt;
		 &lt;h3&gt;{!v.singlePin.Name}&lt;/h3&gt;
		 &lt;div class="singleMeta"&gt;
			&lt;span&gt;{!v.singlePin.Region__c}, &amp;nbsp;&lt;/span&gt;
			&lt;span&gt;{!v.singlePin.Continent__c}&lt;/span&gt;
		 &lt;/div&gt;
		 &lt;div class="singleContent"&gt;
			 &lt;aura:unescapedHtml value="{!v.singlePin.Content__c}" /&gt;
		 &lt;/div&gt;
		 &lt;forceChatter:publisher context="RECORD" recordId="{!v.singlePin.Id}" /&gt;
		 &lt;forceChatter:feed type="Record" subjectId="{!v.singlePin.Id}" /&gt;
	&lt;/div&gt;
	&lt;aura:set attribute="else"&gt;
		&lt;h3 class="pinstitle"&gt;Locations&lt;/h3&gt;
		&lt;aura:iteration items="{!v.pins}" var="pin"&gt;
		&lt;lightning:button class="pinListItem" name="{!pin.Id}" onclick="{!c.markerClick}" &gt;
			&lt;div class="pin-name"&gt;{!pin.Name}&lt;/div&gt;
			&lt;div class="pin-meta"&gt;
				&lt;span class="pin-region"&gt;{!pin.Region__c}&lt;/span&gt;&amp;nbsp;
				&lt;span class="pin-country"&gt;{!pin.Country__c}&lt;/span&gt;
			&lt;/div&gt;
		&lt;/lightning:button&gt;
		&lt;/aura:iteration&gt;
	&lt;/aura:set&gt;
&lt;/aura:if&gt;</code></pre>



<p>Let&#8217;s look at the singlePin scenario first &#8211; which is when an item is clicked. We are simply displaying some data from the singlePin &#8211; along with a button to close it (this <strong>.closeSinglePin() </strong>is not built yet). Then notice the use of&nbsp;&nbsp;<strong>forceChatter:publisher</strong> and&nbsp;<strong>forceChatter:feed</strong>. These components are built in to Lightning &#8211; all we need to do is pass it a Id &#8211; and voila!</p>



<p>This will display our single pin nicely &#8211; along with a Chatter component right below it.</p>



<p><br><img decoding="async" width="534" height="551" class="alignnone size-full wp-image-6664" style="border: 1px solid #ededed;" src="https://michaelsoriano.com/wp-content/uploads/2018/07/chatter.gif" alt="Locations with Chatter"><br></p>



<p>For the list, you see how we wrapped each pin as a <strong>lightning:button</strong>? That&#8217;s because we can click each item and something will happen. We have a <strong>c.markerClick </strong>handler on each item that we haven&#8217;t built yet.<br>The rest is simply markup of the pin details. Let&#8217;s create that handler.</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">markerClick : function(component, event, helper){
	var markerId = event.getSource().get("v.name");
	var marker = helper.markerList[markerId];
	var markerGroupId = marker['__parent']['_group']['_leaflet_id'];
	var parentChildCount = marker['__parent']['_childCount'];
	if(parentChildCount &gt; 1){
		 for(var i=0; i&lt;helper.markerGroups.length; i++){
			if(helper.markerGroups[i]._leaflet_id == markerGroupId){
				helper.markerGroups[i].zoomToShowLayer(marker, function () {
					console.log('zoomed to layer');
					marker.fire('click')
					marker.openPopup();
				});
			}
		 }
	}else{
		marker.fire('click')
		marker.openPopup();
	}
}</code></pre>



<p>Remember, each item in our list &#8211; when clicked, will have the same behavior as clicking the pin (marker) on the map. So the above code is simply a trigger (using <strong>marker.fire()</strong>). But the check before that is to determine &#8211; if the marker is inside a cluster. If it is &#8211; we zoom to the layer first (using&nbsp;<strong>.zoomToShowLayer()</strong>), then fire.</p>



<p>Also, remember that we don&#8217;t have any behavior yet in when we click our pins (we have it as a TODO in our <strong>.buildMap()</strong> function above). Let&#8217;s go back and create that now.</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">function onMarkerClick(e){
	component.set('v.singlePin',{});  //resets to {}
	var id = e.target.options.alt;
	var latLong = e.target.getLatLng();
	if(e.target['_preSpiderfyLatlng']){
		latLong = e.target['_preSpiderfyLatlng']
	}
	map.setView(latLong);
	var pins = component.get('v.pins');
	for(var x=0; x&lt;pins.length;x++){
		if(pins[x].Id == id){
			component.set('v.singlePin',pins[x]);
		}
	}
}</code></pre>



<p>The handler above reset&#8217;s the singlePin to an empty object. Then we get the lat and long from the marker (we get it from &#8220;<strong>e</strong>&#8221; &#8211; or the <strong>event</strong>). We set the view on the map according to the coordinates.&nbsp; Then finally &#8211; we loop through our pins &#8211; and set the <strong>v.singlePin&nbsp;</strong>into the one clicked.</p>



<p>Lastly, the&nbsp;<strong>.closeSinglePin() </strong>function:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">closeSinglePin : function(component, event, helper){
        var map = helper.map;
        var defaultChatterFeed = component.find('defaultChatterFeed');
        var singlePin = component.find('singlePin');
        $A.util.removeClass(defaultChatterFeed, 'slds-hide');
        $A.util.addClass(singlePin, 'slds-hide');
        component.set('v.singlePin',{});
	map.setView([48.85661400000001, 2.3522219], 2);
        map.closePopup();
    },</code></pre>



<p>The above simply hides our Single Pin and Chatter, resets the singlePin variable, then resets the map.</p>



<h3 class="wp-block-heading" id="the-form">The Form</h3>



<p>Finally, we come to the data entry section &#8211; where it will make it easy for us to add pins to our map. This form will have the necessary fields for our custom object &#8211; but most importantly, the auto-suggest field &#8211; that will auto populate the longitude, latitude, region etc.</p>



<p><img decoding="async" width="747" height="326" class="alignnone size-full wp-image-6665" style="border: 1px solid #ededed;" src="https://michaelsoriano.com/wp-content/uploads/2018/07/cities.gif" alt="City autocomplete"></p>



<p><br>I wrote a <a href="https://michaelsoriano.com/lightning-component-google-places/">tutorial</a> about this Auto-suggest field &#8211; using Google Places API, so I&#8217;m not going to get into that code. I&#8217;m simply going to go through the rest of the logic &#8211; that makes our form.</p>



<p>First off, we&#8217;re showing our form in a modal. Still in the same component, let&#8217;s add this to the lower section of our <strong>.cmp</strong> file:</p>



<pre class="wp-block-code"><code lang="markup" class="language-markup">&lt;lightning:button name="openNewPin" onclick="{!c.openNewPin}"&gt;Add New&lt;/lightning:button&gt;
&lt;aura:attribute name="newPin" type="Pin__c" default="{}"/&gt;
&lt;aura:attribute name="predictions" type="List" default="[]"/&gt;
&lt;aura:attribute name="validationErrors" type="List" default="[]"/&gt;
&lt;div role="dialog" tabindex="-1" aura:id="newPinModal" class="slds-modal"&gt;
	&lt;div class="slds-modal__container"&gt;
		&lt;div class="slds-modal__header"&gt;
			&lt;button class="slds-button slds-modal__close slds-button--icon-inverse" title="Close" onclick="{!c.closenewPin}"&gt;&lt;span class="slds-assistive-text"&gt;Close&lt;/span&gt;
			&lt;/button&gt;
			&lt;h2 id="header43" class="slds-text-heading--medium"&gt;Add New Pin&lt;/h2&gt;
		&lt;/div&gt;
		&lt;div class="slds-modal__content slds-p-around--medium"&gt;
			&lt;div&gt;
				&lt;aura:if isTrue="{!v.validationErrors.length &gt; 0}"&gt;
				&lt;ul class="validationErrorList"&gt;
				&lt;aura:iteration items="{!v.validationErrors}" var="validationError"&gt;
					&lt;li&gt;{!validationError}&lt;/li&gt;
				&lt;/aura:iteration&gt;
				&lt;/ul&gt;
				&lt;/aura:if&gt;
				&lt;lightning:input label="Name" value="{!v.newPin.Name}"/&gt;
				&lt;label style="margin:12px 0 3px; display:block;"&gt;Content&lt;/label&gt;
				&lt;lightning:inputRichText value="{!v.newPin.Content__c}" /&gt;
				&lt;div style="position:relative;"&gt;
				&lt;lightning:input label="City"
					value="{!v.newPin.City__c}"
					onchange="{!c.getCities}" /&gt;
					&lt;aura:if isTrue="{!v.predictions.length &gt; 0}"&gt;
						&lt;ul class="city_predictions"&gt;
							&lt;aura:iteration items="{!v.predictions}" var="prediction"&gt;
							&lt;li class="slds-listbox__item"&gt;
								&lt;a onclick="{!c.getCityDetails}" data-placeid="{!prediction.place_id}"&gt;{!prediction.description}&lt;/a&gt;
							&lt;/li&gt;
							&lt;/aura:iteration&gt;
						&lt;/ul&gt;
					&lt;/aura:if&gt;
				&lt;lightning:input value="{!v.newPin.Lat__c}" class="slds-hide" /&gt;
				&lt;lightning:input value="{!v.newPin.Long__c}" class="slds-hide"/&gt;
				&lt;lightning:input value="{!v.newPin.Region__c}" class="slds-hide"/&gt;
				&lt;lightning:input value="{!v.newPin.Country__c}"  class="slds-hide"/&gt;
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/div&gt;
		&lt;div class="slds-modal__footer"&gt;
			&lt;lightning:button onclick="{!c.saveRecord}"&gt;Submit&lt;/lightning:button&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;!--end new pin--&gt;
&lt;div class="slds-backdrop " aura:id="Modalbackdrop"&gt;&lt;/div&gt;
&lt;lightning:notificationsLibrary aura:id="notifLib"/&gt;&lt;!--toast message--&gt;</code></pre>



<p>The&nbsp;<strong>lightning:button </strong>in the beginning, is what opens up the modal. It&#8217;s up to you how you want to show this button, and who to show it to.</p>



<p>The next three lines you&#8217;ll notice that we&#8217;re setting some new attributes. Again, these act as a containers for the variables we&#8217;re using in our component.</p>



<p>The SLDS dialog box contain a close button, a header, a footer&nbsp; and a backdrop. The close button triggers a function&nbsp;<strong>closenewPin()&nbsp;</strong>when clicked. We&#8217;ll get to this later. Then we have a validation error list, then the form fields.</p>



<p>The &#8220;City&#8221; has an onchange handler called&nbsp;<strong>getCities() </strong>&#8211; which grabs the predictions from our Apex class. Again this was in a <a href="https://michaelsoriano.com/lightning-component-google-places/">previous tutorial</a> and find out how it&#8217;s done there.&nbsp; The&nbsp;<strong>lightning:input </strong>fields that have a class &#8220;<strong>slds-hide</strong>&#8221; &#8211; are hidden fields. These fields are automatically filled &#8211; and will require no user interaction.</p>



<p>Finally, the&nbsp;<strong>lightning:button&nbsp;</strong>that takes care of saving the record.</p>



<p>Oh, we also have&nbsp;<strong>lightning:notificationsLibrary </strong>at the very end &#8211; which is Salesforce&#8217;s toast notification. We use this for messaging when successful.</p>



<p><strong>Saving the Record</strong></p>



<p>First let&#8217;s take care of the entering our record. Open the controller and add the method &#8220;<strong>saveRecord</strong>&#8221; below:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">saveRecord : function(component, event, helper){
	var newPin = component.get('v.newPin');
	var str = JSON.stringify(newPin);
	//validation
	var errors = [];
	if(!newPin['Name']){
		errors.push('Name cannot be empty');
	}
	if(!newPin['City__c']){
		errors.push('City cannot be empty');
	}
	component.set('v.validationErrors', errors);
	if(errors.length &gt; 0){
		return false;
	}
	helper.callServer(component,"c.savePin",function(data){
		helper.callServer(component,'c.getPins', function(response){
			component.set('v.Pins',response);
			component.set('v.newPin',{});
			helper.buildMap(component, event, helper, response);
			helper.closeModal(component, 'newPinModal');
			helper.showToast(component, 'success', 'A Pin has been added.');
		},{});
	},{"strsRecord" : str});
},</code></pre>



<p>This function gets called as soon as the user clicks the &#8220;<strong>Save</strong>&#8221; button. First, we do some validation &#8211; and see if our required fields are filled in. If not, we set the <strong>validationErrors&nbsp;</strong>array up and exits.</p>



<p>Otherwise, we do a <strong>.callServer() </strong>twice. First to save the pin &#8211; and inside it&#8217;s callback, we do another&nbsp;<strong>.callServer() </strong>to get all of the pins. This is so that once we add a new pin &#8211; it grabs the new set of pins &#8211; so we can see it right away on the map. Remember I discussed about the <strong>.callServer() </strong>function in &#8220;<a href="https://michaelsoriano.com/call-apex-from-lightning-components/">Call Apex from Lightning components</a>&#8220;.</p>



<p>This is the part that actually does the saving to the database. Add this to your Apex class &#8211; as another method:</p>



<pre class="wp-block-code"><code lang="java" class="language-java">@AuraEnabled
public static Pin__c savePin(String strsRecord){
	Pin__c strsRecord2 = (Pin__c)JSON.deserialize(strsRecord,Pin__c.class);
	try{
		if(strsRecord2 != null){
			insert strsRecord2;
		}
	}catch (Exception ex){
	}
	return strsRecord2;
}</code></pre>



<p>Finally, we call&nbsp;<strong>.buildMap(), .closeModal() </strong>and <strong>.showToast(). </strong>You get what those functions are doing. Let&#8217;s add what&#8217;s missing in our helper. Open <strong>PinHelper.js</strong>&nbsp;and add the code below:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">closeModal : function(component, modal){
	var cmpTarget = component.find(modal);
	var cmpBack = component.find('Modalbackdrop');
	$A.util.removeClass(cmpBack,'slds-backdrop--open');
	$A.util.removeClass(cmpTarget, 'slds-fade-in-open');
},
openModal : function(component,modal){
	var cmpTarget = component.find(modal);
	var cmpBack = component.find('Modalbackdrop');
	$A.util.addClass(cmpTarget, 'slds-fade-in-open');
	$A.util.addClass(cmpBack, 'slds-backdrop--open');
},
showToast : function(component, variant, msg){
	var toastSettings = {
		"title": variant,
		"message": msg,
		"variant" : variant,
		"mode" : "dismissable"
	}
	component.find('notifLib').showToast(toastSettings);
},</code></pre>



<p>The helper functions above are pretty self-explanatory. For further explanation &#8211; especially about the utilities available in the $A object &#8211; refer to this <a href="https://salesforce.stackexchange.com/questions/54032/what-are-the-available-a-javascript-functions-in-lightning">question</a> in StackOverflow.</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">closeNewPin : function(component, event, helper){
		helper.closeModal(component, 'newPinModal');
},
openNewPin : function(component, event, helper){
	helper.openModal(component, 'newPinModal');
},
closeSinglePin : function(component, event, helper){
	helper.closeSinglePin(component, event, helper)
},</code></pre>



<p>Lastly, the above methods are in our helper, but we need to access them using our controller. Simply add the code below to our controller:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">closeNewPin : function(component, event, helper){
		helper.closeModal(component, 'newPinModal');
},
openNewPin : function(component, event, helper){
	helper.openModal(component, 'newPinModal');
},
closeSinglePin : function(component, event, helper){
	helper.closeSinglePin(component, event, helper)
},</code></pre>



<p>And after all is in place, our form should now be working:</p>



<p><img decoding="async" width="633" height="547" class="alignnone size-full wp-image-6700" src="https://michaelsoriano.com/wp-content/uploads/2018/07/form.gif" alt="Form"></p>



<h3 class="wp-block-heading" id="conclusion">Conclusion</h3>



<p>There you have it. A cool map with events, clusters, a custom form &#8211; a good start of a real world application you can build inside Salesforce. Keep in mind these are mostly using Lightning &#8211; unless we had to build it ourselves. </p>



<p>Next up should be filtering the results, adding a better Hover callout on the pins, maybe even having the latest chatter in the hover. The possibilities are endless.</p>
<p>The post <a href="https://michaelsoriano.com/lets-build-map-application-using-leaflet-and-lightning-components/">Let&#8217;s build a Map Application using Leaflet and Lightning Components</a> appeared first on <a href="https://michaelsoriano.com">Michael Soriano</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://michaelsoriano.com/lets-build-map-application-using-leaflet-and-lightning-components/feed/</wfw:commentRss>
			<slash:comments>25</slash:comments>
		
		
			</item>
	</channel>
</rss>
