My ASP.NET MVC 3 project uses jQuery Mobile 1.0 Alpha 3 and Google Maps API 3. My data source is an XML file which contains a list of 10 Information Technology companies in Silicon Valley. A page is generated for each company and their headquarters are displayed on a map. There were a couple of gotchas I came across when implementing Google Maps with jQuery Mobile which I will discuss later on in the post.
Model
First, I created a Location.cs class based on the structure of my Locations.xml file.
I also created a LocationModel.cs class which uses LINQ to read the XML file and append each Location to a List of Location objects.
12345678910
publicLocationModel(){// Linq to read XML data and create a List of Location objectsXDocumentlocationMetaData=XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Locations.xml"));varlocations=fromlocationinlocationMetaData.Descendants("location")selectnewLocation((int)location.Element("id"),location.Element("name").Value,location.Element("address").Value,location.Element("city").Value,location.Element("state").Value,location.Element("zip").Value);this.AddRange(locations.ToList<Location>());}
Controller
I have one Controller named HomeController.cs which creates an instance of LocationModel at the class level and returns all locations to the Index View. It also looks up specific locations for the Map View based on the unique ID passed in the url to the Map method.
LocationModelallLocs=newLocationModel();publicActionResultIndex(){returnView(allLocs);}publicActionResultMap(intid){Locationloc=this.getLocation(id);// if the id does not match any location return error pageif(loc==null){returnView("Error");}else{if(this.lookupGeoCode(loc)){returnView(loc);}else{returnView("Error");}}}// returns the Location object that matches the id parameterprivateLocationgetLocation(intid){Locationresult=allLocs.Find(item=>item.ID==id);returnresult;}
I am using Google’s GeoCoding API to look up the Lat and Long for an address so it can be plotted on the map. I am requesting the results in an xml format and parsing them using LINQ. If the lookupGeoCode() method fails at getting the Lat and Long it returns false and the Map method returns the Error View.
1234567891011121314151617181920212223
// uses Google GeoCoding API to lookup Lat and Long for an address (http://code.google.com/apis/maps/documentation/geocoding/)privateBooleanlookupGeoCode(Locationloc){stringPostUrl="http://maps.googleapis.com/maps/api/geocode/xml?address="+loc.Address+","+loc.City+","+loc.State+"&sensor=false";XDocumentgeo=XDocument.Load(PostUrl);// if an error occurs with the GeoCoding API return falsetry{loc.Lat=(double)(fromgeocodeingeo.Descendants("location")selectgeocode.Element("lat")).SingleOrDefault();loc.Lng=(double)(fromgeocodeingeo.Descendants("location")selectgeocode.Element("lng")).SingleOrDefault();returntrue;}catch{returnfalse;}}
View
I am using the new Razor view engine which is available with ASP.NET MVC 3. I have two Views, Index and Map, which are both strongly-typed. The _Layout.cshtml is my master page and in order to get Google Maps working with jQuery Mobile I had to put the JavaScript reference to the Google Maps API here along with the css style for the map.
_Layout.cshtml
123456789101112131415161718
<!DOCTYPE html><html><head><!--Developed by Greg Jopa on 2/5/2011 - http://www.gregjopa.com/2011/02/asp-net-mvc-3-with-jquery-mobile-and-google-maps/ --><title>@ViewBag.Title</title><linkrel="stylesheet"href="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.css"/><script src="http://code.jquery.com/jquery-1.5.min.js"></script><script src="http://maps.google.com/maps/api/js?sensor=false"></script><script src="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.js"></script><style type="text/css">body{background:#dddddd;}.gmap{height:330px;width:100%;margin:0px;padding:0px}</style></head><body> @RenderBody()
</body></html>
My Index View returns a hyperlink for each location.
Index.cshtml
123456789101112131415161718192021222324
@using jQueryMobileMVC3.Helpers
@model IEnumerable<jQueryMobileMVC3.Models.Location>@{
ViewBag.Title = "jQuery Mobile w/ Google Maps";
}
<divdata-role="page"class="page-home"><divdata-role="header"data-theme="b"><h1>Silicon Valley</h1></div><divdata-role="content"><uldata-role="listview"data-inset="true"> @foreach (var item in Model)
{
<li>@Html.ActionLink((string)item.Name, "Map", "Home", new { id = item.ID }, null).AutoRemoveAlias()</li> }
</ul></div></div>
I am using Html.ActionLink to generate my hyperlinks and talk to my controller. When publishing this ASP.NET MVC Project to my GoDaddy shared hosting I ran into a problem with my hyperlinks including the directory path aliased to my subdomain name which made the url long and ugly. The relative url returned by Html.ActionLink was including /gregjopa/code/ which is aliased to code.gregjopa.com. So my url originally looked like:
I originally tried to fix this by using the URL Rewrite Module available in IIS 7 but I couldn’t get this working with my GoDaddy hosting. So instead I created a helper method to remove the unwanted portion of the url generated by the Html.ActionLink method.
1234567891011121314151617
/* Helper methods to remove the aliased directory added to the url for each hyperlink * Code adapted from the following stackoverflow.com post: http://stackoverflow.com/questions/604814/asp-net-mvc-actionlinks-and-shared-hosting-aliased-domains */publicstaticclassActionLinkHelper{publicstaticstringRemoveAlias(thisMvcHtmlStringlink,stringalias){// Find the href param and replace ...returnlink.ToHtmlString().Replace((alias+"/"),"");}publicstaticMvcHtmlStringAutoRemoveAlias(thisMvcHtmlStringlink){varappRoot=HttpContext.Current.Request.ApplicationPath;returnappRoot!="/"?MvcHtmlString.Create(link.RemoveAlias(appRoot)):MvcHtmlString.Create(link.ToHtmlString());}}
The Map View includes the JavaScript for Google Maps. You cannot use document.ready() when using jQuery Mobile. Instead you must use specific jQuery Mobile page events to inject code into the DOM. My app uses the pagecreate, pageshow, and pagehide events to execute JavaScript. I had to add an id to each page so it could be uniquely referenced in the jQuery Mobile events. Also, in order to have a separate map on each page I had to use unique JavaScript variable names when defining each map.
jQuery Mobile rocks! It is easy to develop with and the themes look great. It also provides a standardized user interface across all popular mobile device platforms. So instead of developing a mobile site with custom code for the iPhone, custom code for Android, custom code for the Windows phone, etc… you can just have one mobile site for all platforms.