Pages

Saturday, January 26, 2013

Using MVC 4 Model objects inside Javascript for Google maps


Using a server-side object as a Model in an MVC 4 View is quite straightforward. Just define the @model at the top of the .cshtml file, and then use it in the rest of the View as needed, taking advantage of the excellent model binding system in MVC 4.

However, using the same Model from within a piece of javascript code is a whole different story. The problem arises because while the rest of the .cshtml file is evaluated on the server side and converted to HTML before being served, the javascript code is sent to the browser and runs on the client side as-is, and does not have access to the Model and its property values.

The solution lies in writing our View such that the Model properties in the .cshtml file are evaluated at the server level itself, and the javascript code simply receives the resulting values and not the property names themselves. Here is a sample code that demonstrates this approach, using the "address" information from a Model object to display a Google Map in the webpage. Using this approach, no changes at all are required in the Controller - the View coding takes care of everything (while not taking over any business logic).

Assuming that a Layout file is being used to support the View, the steps required to display a Google Map on the webpage are:

1. In the "head" section of the Layout, create an @RenderSection, so that our main View can insert the javascript into the "head" of the resulting HTML:


@RenderSection("pagespecificcode", required: false)  


2. In the "body" section of the Layout, call the initialize() method of our javascript when the onload event fires (be careful with this, as it'll fire off a method of that name in any child page, not just the one you want!):


<body onload="initialize()">  



3. In the .cshtml of our specific View itself:


1:  @section pagespecificcode{  
2:    @*BEGIN - Google Maps calculation and rendering*@  
3:    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />  
4:      
5:    <script type="text/javascript"  
6:       src="https://maps.googleapis.com/maps/api/js?sensor=false">  
7:    </script>  
8:      
9:    <script type="text/javascript">  
10:      var geocoder;  
11:      var map;  
12:      var address = @Html.Raw(Json.Encode(Model.AddrLine1 + ' ' + Model.AddrLine2 + ' ' + Model.AddrCity + ' ' + Model.AddrState + ' ' + Model.AddrPostalCode))  
13:        
14:      @*This function is called from the 'onload' event of the 'body' section of the Layout*@  
15:      function initialize() {  
16:        geocoder = new google.maps.Geocoder();  
17:        var latlng = new google.maps.LatLng(0.0, 0.0);  
18:        var mapOptions = {  
19:          zoom: 13,  
20:          center: latlng,  
21:          mapTypeId: google.maps.MapTypeId.ROADMAP  
22:        }  
23:        map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);  
24:    
25:        codeAddress();  
26:      }  
27:    
28:      function codeAddress() {  
29:        geocoder.geocode({ 'address': address, 'region': 'US' }, function (results, status) {  
30:          if (status == google.maps.GeocoderStatus.OK) {  
31:            map.setCenter(results[0].geometry.location);  
32:            var marker = new google.maps.Marker({  
33:              map: map,  
34:              position: results[0].geometry.location  
35:            });  
36:          }  
37:        });  
38:      }  
39:    
40:    </script>  
41:    @*END - Google Maps calculation and rendering*@  
42:  }  

The crucial line in the code above is the following one; it is taking the Model properties and converting them to a string, which is then set as the value of the javascript variable. One little drawback here - there's no Intellisense available while we're programming the line.


12:      var address = @Html.Raw(Json.Encode(Model.AddrLine1 + ' ' + Model.AddrLine2 + ' ' + Model.AddrCity + ' ' + Model.AddrState + ' ' + Model.AddrPostalCode))  

4. Later on in the .cshtml file, at the point you want to display the map, the only section that's needed to render the map is this - there's no code, just define the div:


1:  <div class="gmap" id="map_canvas">  
2:  </div>


5. Make sure the CSS file defines the correct size and any other properties you want for the rendered map:


1:  .gmap  
2:  {  
3:    width: 340px;  
4:    height: 180px;  
5:    border: 1px solid lightgray;  
6:  }  

Done. Now once the Model object is populated by the Controller, the View will take care of the rest and render the Google Map nicely.

Caution here: We should not convert the entire object as it creates a large Json string, most of which are likely not needed for our purpose above. They will simply add to your server outbound traffic. Convert only the fields needed, as illustrated above. Checking the Page Source shows exactly what's being converted.

Please note: The above example does not use an API Key from Google. It will work fine without that key; however, Google places limits on how many maps can be rendered, and sites with lots of traffic will need a key. Please see details of the API, the Key, traffic limits and various other aspects of the system on the Google Maps support page: 


Happy coding!

2 comments:

  1. Very useful post, thank you.

    ReplyDelete
  2. Thanks for your topic!Could you upload demo your code,please?

    ReplyDelete