Newer Version Available

This content describes an older version of this product. View Latest

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:

1swfobject.registerObject("clippy.codeblock-0", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17    function addLoadEvent(func) { 
18       var oldonload = window.onload;
19       if (typeof window.onload != 'function') {
20          window.onload = func;
21       } else {
22          window.onload = function() {
23             oldonload();
24             func();
25            }
26        }
27     }
28
29     addLoadEvent(
30     function() {
31        if (GBrowserIsCompatible()) {
32           var my_geocoder = new GClientGeocoder();
33           var map = new GMap2(document.getElementById("map"));
34           var TC = new GMapTypeControl();
35           var bottomRight = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,10));
36           var mCount =0;
37
38           map.addControl(new GSmallMapControl()); // Small arrows
39           map.addControl(TC, bottomRight);  // Map type buttons
40
41           function LTrim( value ) {
42              var re = /\s*((\S+\s*)*)/;
43              return value.replace(re, "$1");
44           }
45
46           function RTrim( value ) {
47              var re = /((\s*\S+)*)\s*/;
48              return value.replace(re, "$1");
49           }
50
51           // Remove leading and ending whitespaces
52           function trim( value ) {
53              return LTrim(RTrim(value));
54           }
55
56           function doAddLocationToMap(SiteName, Street, City, State, Zip, typ) {
57              var addr = Street + ", " + City + ", " + State + " " + Zip;
58              my_geocoder.getLatLng (addr, 
59              function(point) {
60                 if (point) {
61                    var mTag = '';
62                    var myIcon = new GIcon(G_DEFAULT_ICON);
63
64                    if(typ == 'self') {
65                       mTag = "<b>" + SiteName + "</b>" + "<br>" + City ;
66                       myIcon.image = "http://maps.google.com/mapfiles/arrow.png";
67                       myIcon.iconSize=new GSize(32,32);
68                    } else { 
69                       if(typ == 'acct') {
70                          mCount ++;
71                          var priAr = SiteName.split(":"); 
72                          var compName = priAr[0];  // company name
73                          var pri = trim(priAr[1]); // priority
74                          var acctId = priAr[2]; //account id
75                          var index = "";
76                          var imgName = "marker"; // default marker image
77                          var color = ""; 
78
79                          mTag = "<b>" + compName + "</b>" + "<br>"
80                                 + "Priority: " 
81                                 +  pri  + "<br>" + City ;
82                          // Set up marker colors based on priority
83                          if (pri == 'Medium') color="Yellow"; 
84                          else if (pri == 'High') color="Red"; 
85                          else if (pri == 'Low') color="Green";
86
87                          if(mCount>10){ // use default marker
88                             myIcon.image = 
89                                "http://maps.google.com/mapfiles/marker.png";
90                          } else { // use custom marker 1-10
91                             index = String(mCount);
92                             imgName = imgName + color + index + ".png";
93                             myIcon.image = "{!URLFOR($Resource.markers, 
94                                            'markers/" + imgName + "')}";  
95                          }
96
97                          document.getElementById(acctId).src = myIcon.image;
98                          myIcon.iconSize=new GSize(20,34);
99                       }
100                    }
101                    myIcon.shadowSize=new GSize(56,32);
102                    myIcon.iconAnchor=new GPoint(16,32);
103                    myIcon.infoWindowAnchor=new GPoint(16,0);
104                    markerOptions2 = { icon:myIcon };
105                    var marker = new GMarker(point, markerOptions2);
106                    map.setCenter(point, 8);
107                    map.addOverlay(marker);
108
109                    // Set up listener action to show info on click event
110                    GEvent.addListener(marker, "click", 
111                       function() { 
112                          marker.openInfoWindowHtml(mTag); 
113                       }) ;
114                 }
115              }
116              );
117           }
118
119           //Get accts and draw address
120           var arAllStr = '';
121           arAllStr = '{!AddrArStr}'; // Get all address recs 
122           var arLi = arAllStr.split("~::~"); // Split on line break delim
123           for (var i = 0; i < arLi.length-1; i++) {  
124              var arLiStr =arLi[i];
125              var arCols =arLiStr.split("~:~"); //Split  to get columns
126
127              if(arCols[1].length >0)
128                 doAddLocationToMap(arCols[0],arCols[1],arCols[2],
129                                    arCols[3],arCols[4],'acct');     
130           }
131
132           //Get user address and draw
133           doAddLocationToMap('{!$User.FirstName} {!$User.LastName}'
134                 +' (Me)','{!$User.Street}','{!$User.City}','
135                 {!$User.State}','{!$User.PostalCode}','self');
136        } 
137    }
138    );

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

1swfobject.registerObject("clippy.codeblock-1", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<apex:page controller="mapController" showHeader="false">
18    <apex:composition template="iuivf" />
19    <script src="{!myKey}" type="text/javascript"> </script>
20    <apex:includeScript value="{!$Resource.MobileListView}"/>
21
22
23    <ul title="Accounts" selected="true" id="home" >
24      <!-- Draw user name at top of panel -->
25        <li class="group">
26            User: {!$User.FirstName} {!$User.LastName}
27        </li>
28
29        <!-- Create panel for Google Maps object -->
30        <div class="panel" style="padding: 10px;"  >
31              <div id="map" style="width: 300px; height: 300px;">
32                  </div>
33        </div>
34
35        <!-- Create group sub-panel to display list -->
36        <li class="group">Accounts</li>
37
38        <!-- Draw accounts, one per row -->
39        <apex:repeat value="{!MyAccts}" var="p" >
40           <li>
41              <a href="accountDetail?id={!p.Id}" >
42                 <img id="{!p.Id}"
43                      src="http://maps.google.com/mapfiles/marker.png"/>
44                 {!p.Name}
45              </a>
46           </li>
47        </apex:repeat>
48    </ul>
49</apex:page>
50

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:

1swfobject.registerObject("clippy.codeblock-2", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--
18*   Page definition: iuivf
19*   Visualforce template for iUI includes needed for
20*   using the iui framework <http://code.google.com/p/iui/>
21*   in any Visualforce page.
22-->
23
24<apex:page>
25    <meta name="viewport" content="width=320; initial-scale=1.0;
26       maximum-scale=1.0; user scalable=0;"/>
27    <apex:includeScript value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.js')}" />
28    <apex:styleSheet value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.css')}" />
29
30    <style> #home { position: relative; top: 0px; } </style>
31
32</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 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 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.