Sunday, July 6, 2014

Creating a simple address finder using google place autocomplete

There are a number old asp.net forms applications out there with data entry forms. If by any  chance you are bound to use a web system with lots of user demographic information capture then you are left with no choice but to type the details out. Having stated so there are a number of ways you can add value to an existing application and improve user experience without changing much of the existing data capturing fields and associated logic. Using location based services will be one of the quick wins and this post will detail about  creating an address finder user control.

Googles’ Place Autocomplete is used in this post and you do not need to go through the hassle of getting specific API key for non-commercial or low usage scenario. Read google place autocomplete documentation for more information. I have existing asp.net web application which uses address entry fields as seen below
Same address fields are sitting in the application in different areas with different validation criteria’s and logic wired to these fields.  One of the easy ways to bring user friendliness is to introduce an auto address populate control shown as below.

Once the user selects the recommendations the address data will be populated back onto the existing address fields. Here I am not modifying the existing controls or code , instead adding some additional capability to populate address fields faster. This approach mitigates the risk of breaking any existing feature and avoided much of regression test.

We can now look how to achieve this in the following steps  
  • Include the following javascript in masterpage of the application
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false&libraries=places&language=en-AU">
</script>

  • Create a user control AddressAutoComplete.ascx with following mark up
<div>
Address Search<asp:TextBox ID="AddressTextBox" runat="server" Width="450" />
</div>
  • Add the following javascript code to the user control mark up page(we are using query and the following code works on old jquery versions as well)
<script type="text/javascript">
$(function () {
 if (typeof google == 'undefined' || typeof google.maps.places == 'undefined') {
   //Make Sure the Library has loaded. Else Hide the autocomplete Box.
    $("#<%=AddressAutoFillRow.ClientID %>").hide();
  } else {
      //Setting up Auto Complete for Address
  var residentialAutocomplete = new google.maps.places
                                   .Autocomplete($('#<%=AddressTextBox.ClientID %>')[0], {});
  //Australia biased results
  var bounds = new google.maps.LatLngBounds(new google.maps.LatLng(-44.21370990970204,      110.7421875), new google.maps.LatLng(-9.188870084473393, 154.6435546875));

  residentialAutocomplete.setBounds(bounds);
  google.maps.event.addListener(residentialAutocomplete, 'place_changed', function () {

     var place = residentialAutocomplete.getPlace();
     var $data = new Array();
     for (var i = 0; i < place.address_components.length; i++) {
       $data[place.address_components[i].types[0]] = place.address_components[i].long_name;
     }
      var st = '';
      if ($data != undefined) {
          if ($data['street_number'] != undefined) st = $data['street_number'];
          if ($data['route'] != undefined) st += " " + $data['route'];
          if ($data['subpremise'] != undefined) st = $data['subpremise'] + " / " + st;
      }
      $('#<%=this.AddressLine1ClientId %>').val(st);
      $('#<%= this.CityClientId %>').val($data['locality']);
      if ($("#<%=this.CountryClientId %> option:contains('"+$data['country']+"')").length>0){
          $('#<%=this.CountryClientId %> option').filter(function () { 
           return ($(this).text() == $data['country']); }).attr('selected', true);
      } else {
               $('#<%=this.CountryClientId %>').val("");
         }
        $('#<%=this.StateClientId %>').val($data['administrative_area_level_1']);
        $('#<%=this.PostCodeClientId %>').val($data['postal_code']);
      });
     }
   });
</script>

  •  Code behind will look as follows and “ViewStateProperty” attribute from my earlier post is used to maintain state for properties in view state  

public partial class AddressAutoComplete : System.Web.UI.UserControl
{

[ViewStateProperty]
        public string AddressLine1ClientId { get; set; }
        [ViewStateProperty]
        public string CityClientId { get; set; }
        [ViewStateProperty]
        public string StateClientId { get; set; }
        [ViewStateProperty]
        public string PostCodeClientId { get; set; }
        [ViewStateProperty]
        public string CountryClientId { get; set; }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (!IsPostBack)
            {
                this.AddressLine1ClientId = CityClientId = StateClientId 
                  = PostCodeClientId = CountryClientId = string.Empty;
            }
        }
}

Now the control is ready to use in web pages; now we do that by registering control on page and setting the ClientId properties of the user control
<%@ Register Src="~/AddressAutoComplete.ascx" TagName="AddressSearch" TagPrefix="cc" %>
 <cc:AddressSearch ID="AddressAutoCompleteControl" runat="server" />
[the old mark up for address control sits here] 

In the code behind file aspx.cs file
protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
    {
     AddressAutoCompleteControl.AddressLine1ClientId = txtAddress.ClientID;
     AddressAutoCompleteControl.CityClientId = txtCity.ClientID;
     AddressAutoCompleteControl.StateClientId = txtState.ClientID;
     AddressAutoCompleteControl.PostCodeClientId = txtPostCode.ClientID;
     AddressAutoCompleteControl.CountryClientId = ddCountry.ClientID;
   }
}