Building the Map and List View

The next step in building the mapping application is creating the Visualforce page that displays the map and the corresponding list of accounts. The Visualforce page defines a panel for the Google Maps object, creates a group sub-panel to display the list of accounts, and uses JavaScript to retrieve the account addresses and populate the map with color-coded markers based on the customer's priority. The JavaScript sets up the map object by performing the following logic:

  • Get the addresses to map from the {!AddrArStr} string array
  • Unpack the address array by keying off the delimiters defined in the controller
  • Call doAddLocationToMap for all account addresses and the current user
  • Use Account.CustomerPriority__c as the key to determine which marker color to use—green, yellow, or red
  • Retrieve the custom image markers stored in the $Resource.markers static resource

It's good practice to place any JavaScript code within a static resource, in case it needs to be referenced in multiple locations. Create a static resource named MobileListView:

    function addLoadEvent(func) { 
       var oldonload = window.onload;
       if (typeof window.onload != 'function') {
          window.onload = func;
       } else {
          window.onload = function() {
             oldonload();
             func();
            }
        }
     }

     addLoadEvent(
     function() {
        if (GBrowserIsCompatible()) {
           var my_geocoder = new GClientGeocoder();
           var map = new GMap2(document.getElementById("map"));
           var TC = new GMapTypeControl();
           var bottomRight = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,10));
           var mCount =0;

           map.addControl(new GSmallMapControl()); // Small arrows
           map.addControl(TC, bottomRight);  // Map type buttons

           function LTrim( value ) {
              var re = /\s*((\S+\s*)*)/;
              return value.replace(re, "$1");
           }

           function RTrim( value ) {
              var re = /((\s*\S+)*)\s*/;
              return value.replace(re, "$1");
           }

           // Remove leading and ending whitespaces
           function trim( value ) {
              return LTrim(RTrim(value));
           }

           function doAddLocationToMap(SiteName, Street, City, State, Zip, typ) {
              var addr = Street + ", " + City + ", " + State + " " + Zip;
              my_geocoder.getLatLng (addr, 
              function(point) {
                 if (point) {
                    var mTag = '';
                    var myIcon = new GIcon(G_DEFAULT_ICON);

                    if(typ == 'self') {
                       mTag = "<b>" + SiteName + "</b>" + "<br>" + City ;
                       myIcon.image = "http://maps.google.com/mapfiles/arrow.png";
                       myIcon.iconSize=new GSize(32,32);
                    } else { 
                       if(typ == 'acct') {
                          mCount ++;
                          var priAr = SiteName.split(":"); 
                          var compName = priAr[0];  // company name
                          var pri = trim(priAr[1]); // priority
                          var acctId = priAr[2]; //account id
                          var index = "";
                          var imgName = "marker"; // default marker image
                          var color = ""; 

                          mTag = "<b>" + compName + "</b>" + "<br>"
                                 + "Priority: " 
                                 +  pri  + "<br>" + City ;
                          // Set up marker colors based on priority
                          if (pri == 'Medium') color="Yellow"; 
                          else if (pri == 'High') color="Red"; 
                          else if (pri == 'Low') color="Green";

                          if(mCount>10){ // use default marker
                             myIcon.image = 
                                "http://maps.google.com/mapfiles/marker.png";
                          } else { // use custom marker 1-10
                             index = String(mCount);
                             imgName = imgName + color + index + ".png";
                             myIcon.image = "{!URLFOR($Resource.markers, 
                                            'markers/" + imgName + "')}";  
                          }

                          document.getElementById(acctId).src = myIcon.image;
                          myIcon.iconSize=new GSize(20,34);
                       }
                    }
                    myIcon.shadowSize=new GSize(56,32);
                    myIcon.iconAnchor=new GPoint(16,32);
                    myIcon.infoWindowAnchor=new GPoint(16,0);
                    markerOptions2 = { icon:myIcon };
                    var marker = new GMarker(point, markerOptions2);
                    map.setCenter(point, 8);
                    map.addOverlay(marker);

                    // Set up listener action to show info on click event
                    GEvent.addListener(marker, "click", 
                       function() { 
                          marker.openInfoWindowHtml(mTag); 
                       }) ;
                 }
              }
              );
           }

           //Get accts and draw address
           var arAllStr = '';
           arAllStr = '{!AddrArStr}'; // Get all address recs 
           var arLi = arAllStr.split("~::~"); // Split on line break delim
           for (var i = 0; i < arLi.length-1; i++) {  
              var arLiStr =arLi[i];
              var arCols =arLiStr.split("~:~"); //Split  to get columns

              if(arCols[1].length >0)
                 doAddLocationToMap(arCols[0],arCols[1],arCols[2],
                                    arCols[3],arCols[4],'acct');     
           }

           //Get user address and draw
           doAddLocationToMap('{!$User.FirstName} {!$User.LastName}'
                 +' (Me)','{!$User.Street}','{!$User.City}','
                 {!$User.State}','{!$User.PostalCode}','self');
        } 
    }
    );

The following code defines the landing page of our mapping application:

<apex:page controller="mapController" showHeader="false">
    <apex:composition template="iuivf" />
    <script src="{!myKey}" type="text/javascript"> </script>
    <apex:includeScript value="{!$Resource.MobileListView}"/>


    <ul title="Accounts" selected="true" id="home" >
      <!-- Draw user name at top of panel -->
        <li class="group">
            User: {!$User.FirstName} {!$User.LastName}
        </li>

        <!-- Create panel for Google Maps object -->
        <div class="panel" style="padding: 10px;"  >
              <div id="map" style="width: 300px; height: 300px;">
                  </div>
        </div>

        <!-- Create group sub-panel to display list -->
        <li class="group">Accounts</li>

        <!-- Draw accounts, one per row -->
        <apex:repeat value="{!MyAccts}" var="p" >
           <li>
              <a href="accountDetail?id={!p.Id}" >
                 <img id="{!p.Id}"
                      src="http://maps.google.com/mapfiles/marker.png"/>
                 {!p.Name}
              </a>
           </li>
        </apex:repeat>
    </ul>
</apex:page>

The markup in our page uses the <apex:composition> component to reference a template. The template leverages the iUI framework, which lets us apply iPhone-like styling to our page. The iUI framework is included from the $Resource.IUI static resource. By defining a template, we can easily apply the same styling to all of the Visualforce pages we create for the iPhone platform.

The following markup defines the iuivf page used as the template:

<!--
*   Page definition: iuivf
*   Visualforce template for iUI includes needed for
*   using the iui framework <http://code.google.com/p/iui/>
*   in any Visualforce page.
-->

<apex:page>
    <meta name="viewport" content="width=320; initial-scale=1.0;
       maximum-scale=1.0; user scalable=0;"/>
    <apex:includeScript value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.js')}" />
    <apex:styleSheet value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.css')}" />

    <style> #home { position: relative; top: 0px; } </style>

</apex:page>

Note the following about the template:

  • The markup overrides the #home style from the iUI library. This ensures that our application will render in Salesforce Classic Mobile without any noticeable gap at the top of the page.
  • The markup avoids the use of the class="Toolbar" element. The embedded browser in Salesforce Classic Mobile has a navigation toolbar at the top of the page, so a second toolbar would likely confuse users. If you want to use the button styles provided in the iUI framework, don't use the Toolbar class to render the buttons.
Previous
Next