# Displaying multiple Google Maps markers on a Visualforce page

I hope this is the right board for this. I am a novice programmer and I am trying to learn how to write better code. I have a visualforce page with a very long Javascript script in the middle of it to display a Google Map. The code is entirely inside of a single function, but I cannot figure out a cleaner way to break it up. What are some ways that I can write code cleaner and better?

<apex:page docType="html-5.0" standardController="Property__c" extensions="ControllerJSGeo">

<apex:includeScript value="https://c.xxx.visual.force.com/resource/1466633623000/connection"/>
<script type="text/javascript" src="/soap/ajax/24.0/apex.js"></script>

<script type="text/javascript">

$(document).ready(function() { //set map variables var myOptions = { zoom: 18, mapTypeId: google.maps.MapTypeId.HYBRID, mapTypeControl: true, scrollwheel: false } var map; var marker; var marker2; var propLat; var propLong; var geocoder = new google.maps.Geocoder(); var address = "{!Property__c.Property_Address__c}, " + "{!Property__c.City__c}, " + "{!Property__c.State__c} " + "{!Property__c.Zip_Postal_Code__c}}"; var infowindow = new google.maps.InfoWindow({ content: "<b>{!Property__c.Name}</b><br>{!Property__c.Property_Address__c}<br>{!Property__c.City__c}, {!Property__c.State__c} {!Property__c.Zip_Postal_Code__c}" }); //get the Tenant Subtype, removing any unsafe characters such as quotes var dirtyST="{!Property__c.Tenant_Sub_Type__c}"; var subtype=cleanQuotes(dirtyST); function cleanQuotes(unsafe) { return unsafe .replace(/'/g,"\\'"); } //get geocoordinates of each property geocoder.geocode( { address: address}, function(results, status) { if (status == google.maps.GeocoderStatus.OK && results.length) { if (status != google.maps.GeocoderStatus.ZERO_RESULTS) { //create map map = new google.maps.Map(document.getElementById("map"), myOptions); map.setTilt(45); //center map map.setCenter(results[0].geometry.location); //create marker marker = new google.maps.Marker({ position: results[0].geometry.location, map: map, title: "{!Property__c.Name}" }); //add listeners google.maps.event.addListener(marker, 'click', function() { infowindow.open(map,marker); }); google.maps.event.addListener(infowindow, 'closeclick', function() { map.setCenter(marker.getPosition()); }); //if the geocoord fields are blank, create new Salesforce object from record, and set the geocoord fields to geocoordinates from result propLat=results[0].geometry.location.lat(); propLong=results[0].geometry.location.lng(); var pLat="{!Property__c.GeoCoord__latitude__s}"; var pLng="{!Property__c.GeoCoord__longitude__s}"; if((!pLat||!pLng||pLat==""||pLng=="")&&(!address||address!="")){ var newRecords = []; var c = new sforce.SObject("Property__c"); var pId="{!Property__c.Id}"; c.Id=pId; c.GeoCoord__latitude__s=propLat; c.GeoCoord__longitude__s=propLong; //Then update the record and reload the page newRecords.push(c); result = sforce.connection.update(newRecords); reload(); } else{ console.log("geocoding failed for"+pId+"::"+address+"...Address is null or geocoords already exist"); } //Get the properties returned by the Controller, and create a Marker object for each one var geosArray = []; var geosObject={}; var geos=[]; var idy=0; <apex:repeat value="{!getgeoList}" var="m"> geosArray[idy++]='{!m}'; </apex:repeat> for (var g = 0; g < geosArray.length; ++g) { geosObject=JSON.parse(geosArray[g]); geos.push(geosObject); } for (var i = 0; i < geos.length; ++i) { var myLatLng = {lat: geos[i].GeoCoord__c.latitude, lng: geos[i].GeoCoord__c.longitude}; var html="<b>Nearby {!Property__c.Tenant_Sub_Type__c}:</b><br /><br /></b><a href='/"+geos[i].Id+"'target='_blank'>"+geos[i].Name+"</a><i><br />"+geos[i].Property_Address_Full__c+"</i><br />"; createMarker(myLatLng, html); } function createMarker(latlng, html) { marker2 = new google.maps.Marker({ position: latlng, map: map, icon:'http://maps.google.com/mapfiles/ms/icons/blue-dot.png' }); addInfo(marker2,html); } function addInfo(marker2,html) { var infowindow2 = new google.maps.InfoWindow({ content: html }); google.maps.event.addListener(marker2, 'click', function() { infowindow2.open(map, marker2); }); } //Brackets for OK results } } else {$('#map').css({'height' : '15px'});
$('#map').html("Oops! {!Property__c.Name}'s billing address could not be found, please make sure the address is correct."); resizeIframe(); } }); // resize iFrame var me = window.name; if (me) { var iframes = parent.document.getElementsByName(me); if (iframes && iframes.length == 1) { height = document.body.offsetHeight; iframes[0].style.height = height + "px"; } } }); </script> <style> #map { font-family: Arial; font-size:12px; line-height:normal !important; height:800px; background:transparent; } </style> </head> <body> <div id="map"></div> <apex:form id="distance"> <apex:outputText >Find nearby properties within&nbsp;</apex:outputText> <apex:input value="{!dist}" label="X" type="number"/> <apex:outputText >&nbsp;miles&nbsp;&nbsp;&nbsp;&nbsp;</apex:outputText> <apex:commandButton value="Submit" action="{!getgeoList}"/> </apex:form> </body> <br /><br /> <apex:outputText >====DEBUG====</apex:outputText><br /> <apex:outputText >Distance:</apex:outputText> <apex:outputText >{!dist}</apex:outputText><br /><br /> <apex:outputText >{!getgeoList}</apex:outputText> <apex:form > <apex:actionFunction action="{!reload}" name="reload"/> </apex:form> </apex:page> ## 1 Answer Sure, there are many things you can clean up just in your javascript. • Always develop in strict mode. • Always use Immediately Invoked Function Executions. • You might not need jQuery • If you decide you do need jQuery, always use noConflict. • Always use JSLint • It mostly dings you for trailing space here • You can one-line your var declarations. And specific to the Salesforce platform: • All strings used in your Javascript should be encoded with JSENCODE. • You don't need to reinvent the wheel with your cleanQuotes function, and it will likely delay or fail security review. • When passing arrays between server side and client side, use serialization and parsing instead of mixing markup into your scripts with an <apex:repeat> tag. Using the above, your code might look more like: (function (w,$) {
"use strict";
$(function () { var map, marker, marker2, propLat, propLong, myOptions = {...}, geocoder = new google.maps.Geocoder(), address = '{!JSENCODE(Property__c.Property_Address__c) & JSENCODE(...) & etc.}'; // and other properties geocoder.geocode(..., function () { // this function can be declared separately // instead of anonymously }); }); }(window, jQuery.noConflict())); You can also break out some of the functions you have declared into top level var declarations. (function (w,$) {
"use strict";
\$(function () {
var /*other variables*/,
createMarker = function (latlng, html) {
// logic here
},
addInfo = function (marker2, html) {
// logic here
};
});
}(window, jQuery.noConflict()));

You might also consider geocoding your data in a batch process, at which point you can use the out of the box <apex:map> and <apex:mapMarker> tags. They are supported for addresses you haven't geocoded yet, but you only get ten markers in that case. It looks like there is now also a tag for <apex:mapInfoWindow>

• Thanks. Very helpful! So <apex:mapMarker> will work even without the geocoordinates? – MRG Oct 11 '16 at 16:30
• Yes, it will work, but you can only use ten of them in that case. – Adrian Larson Oct 11 '16 at 16:45
• I haven't used JSENCODE before. I had to change your code to: {!JSENCODE(Property__c.Property_Address__c)} etc... to get it to work. – MRG Oct 11 '16 at 19:42
• Good catch, yes you still need merge syntax. Post updated. – Adrian Larson Oct 11 '16 at 20:33