I have had alot of requests of how to get the city and state fields of forms to auto fill based on a zip code. Now there are many ways to accomplish this
1. Building you own database: Possible if you plan to use your site in a condensed geographical area, overwelming task if you plan to go national or international.
2. Downloading csv or txt files and importing them into a database or xml doc: If you want the ability to update or correct errors quikly this is the way to go, but it would be up to you to update and you would use your database or site space to store the info.
3.Using a web service: This is the one I use and will be showing you how in the article.
Using a Web Service to auto fill City and State fields in a form or table.
For this example I will be using GeoNames, this is a free service here are there terms and conditions. It allows uptp 50,000 requests a day, so if you plan on needing more then that (your doing better then most) then they do offer a commercial web service.
This example was done Using VS2010 Beta2 and therefore uses the new clientIDmode feature. If you decide to use VS2008 the only problem you may run into is getting the values from the dropdown and textboxes. //Look at commented code for modifications needed using VS2008
File structue for this example:
- CityStateSevice.asmx
- CityState.vb
- CityStateSearch.js
- Address.aspx : This can be named anything you want.
Creating the Webservice and common files.
Step1: Setting up CityStateService.asmx
In the root of your website right click and choose Add New Item find 'Web Service' and select it , name it and check PlaceCode in sepearate file .
This will add the CityStateSevice.asmx in the root and also craete the CityStateService.vb in the app_code folder.
Open the CityStateService.vb file and replace the existing code with this code. This is what makes the call to the GeoNames webservice.
//In VS2008 add Imports System.ComponentModel
Imports System
Imports System.Web.Script.Services
Imports System.Web.Services
Imports BWS
' To allow this Web Service to be called from script using ASP.NET AJAX,
' * we need to set the following attribute.
' To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
<System.Web.Script.Services.ScriptService()> _ <System.Web.Services.WebService(Namespace:="BWS")> _ <System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ToolboxItem(False)> _
Public Class CityStateService
Inherits WebService
<WebMethod()> _
Public Function GetCityState(ByVal postalcode As String, ByVal country As String) As String
Return CityState.GetCityState(postalcode, country)
End Function
End Class
Step2: Setting up CityState.vb
In the App_Code folder rightclick and choose Add New Item find 'Class" , select and name it.
This is the code that will recieve the request from the javascript file, process both the request for the servive and the returned results.(basically a middle man)
Copy and paste this code:
Imports System
Imports System.Net
Imports System.Globalization
Imports System.IO
Namespace BWS
Public Class CityState
Private Shared ReadOnly FindCityStateUrl As String = "http://ws.geonames.org/postalCodeLookupJSON?postalcode={0}&country={1} "
Public Shared Function GetCityState(ByVal postalcode As String, ByVal country As String) As String
Dim formattedUri As String = [String].Format(CultureInfo.InvariantCulture, FindCityStateUrl, postalcode, country) Dim webRequest As HttpWebRequest = GetWebRequest(formattedUri) Dim response As HttpWebResponse = DirectCast(webRequest.GetResponse(), HttpWebResponse) Dim jsonResponse As String = String.Empty Using sr As New StreamReader(response.GetResponseStream())
jsonResponse = sr.ReadToEnd()
End Using
jsonResponse = jsonResponse.Replace("[", "") jsonResponse = jsonResponse.Replace("]", "") Return jsonResponse
End Function
Private Shared Function GetWebRequest(ByVal formattedUri As String) As HttpWebRequest
' Create the request URI.
Dim serviceUri As New Uri(formattedUri, UriKind.Absolute)
' Return the HttpWebRequest.
Return DirectCast(System.Net.WebRequest.Create(serviceUri), HttpWebRequest)
End Function
End Class
End Namespace
Step3: Setting up CityStateSearch.js
If you do not have a folder named Scripts create one in the root of the website. then rightclick on it and choose Add New Item find 'Jscript File" , select and name it.
What I did here is crate a generic javascript file that can recieve the postalcode and countrycode from any page and return it. This way you can call it from multiple pages without adding the javascript in each page. As you can see it handles the info from the page and handle 2 errors, zip code not existing and webservice error.
Copy and paste this code:
function callCityStateSearch(zipcodevalue, countryvalue) {
CityStateService.GetCityState(zipcodevalue, countryvalue
, GetCityState_success, onFailed);
}
function GetCityState_success(e) {
try { var result = Sys.Serialization.JavaScriptSerializer.deserialize(e, true);
}
catch (err) {
var result = "false"
}
if (result == "false") { alert("Zip code does not exist.");
}
else {
handleResult(result)
}
}
function onFailed() { alert("Web Service Error");
}
Step4: Add the Service and Script to the ScriptManager
If your website is using Master pages you will need to add this just after the <form> tag, otherwise add it to the aspx page it will be used in.
Do not copy and paste this if you alraedy have a ScriptManager just add the 2 refferences.
<asp:ScriptManager ID="sm" runat="server">
<Services>
<asp:ServiceReference Path="~/CityStateService.asmx" />
</Services>
<Scripts>
<asp:ScriptReference Path="~/Scripts/CityStateSearch.js" />
</Scripts>
</asp:ScriptManager>
Thats it for setting up the Webservice, it is now ready to be added to and used by any page requesting addresses.
Using the Webservice in a page.
This example will use a table based template. this is to show you how to interact with the service and can be implemented in many ways.
Step1: Setting up the Table
This is a simple table with labels and textboxes and a dropdown for the country selection. This example will have a few Countries added statically, but you could also get a country list either from a database or (you guessed it) a webservice. There are about 240 contries used in this webservice.
//If using VS2008 get rid of ClentIdMode="Static" and add runat="server" to table rows with id's ziprow,cityrow,and staterow.
<table style="border-style: inset; background-color: #CCCCCC;">
<tr>
<td>
<asp:Label ID="Countrylbl" runat="server" Text="Country"></asp:Label>
</td>
<td>
<asp:DropDownList ID="CountryDD" ClientIDMode="Static" runat="server">
<asp:ListItem Value="xx" Text="Select Country" />
<asp:ListItem Value="US" Text="United States" />
<asp:ListItem Value="FR" Text="France" />
<asp:ListItem Value="GB" Text="United Kingdom" />
<asp:ListItem Value="IT" Text="Italy" />
<asp:ListItem Value="MX" Text="Mexico" />
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
<asp:Label ID="ZipCodelbl" runat="server" Text="ZipCode:"></asp:Label>
</td>
<td id="ziprow" clientidmode="Static" style="visibility: hidden;">
<asp:TextBox ID="ZipCodetb" ClientIDMode="Static" onblur="processrequest()" Text=""
runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="Address1lbl" runat="server" Text="Address 1:"></asp:Label>
</td>
<td>
<asp:TextBox ID="Address1tb" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="Address2lbl" runat="server" Text="Address 2:"></asp:Label>
</td>
<td>
<asp:TextBox ID="Address2tb" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="Citylbl" ClientIDMode="Static" runat="server" Text="City:"></asp:Label>
</td>
<td id="cityrow" clientidmode="Static" style="visibility: hidden;">
<asp:TextBox ID="Citytb" ClientIDMode="Static" Text="" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Label ID="Statelbl" ClientIDMode="Static" runat="server" Text="State:"></asp:Label>
</td>
<td id="staterow" clientidmode="Static" style="visibility: hidden;">
<asp:TextBox ID="Statetb" ClientIDMode="Static" Text="" runat="server"></asp:TextBox>
</td>
</tr>
</table>
Notice the zipcoderow, cityrow, and staterow are set to hidden. This because the first thing I want the user to do is select a country.
Step2: Make ZipCodetb Visible when country selected.
Simply add Script and in the Codebehind add the following code to the Countrydd Page Init.
//If using VS2008 change "ziprow" to '<%= ziprow.ClientID %>' and 'ZipCodetb' to '<%= ZipCodetb.ClientID %>'
<script type="text/javascript">
function onSelectionChanged() { document.getElementById("ziprow").style.visibility = "visible"; document.all('ZipCodetb').focus();
}
</script>
CodeBehind:
Protected Sub CountryDD_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles CountryDD.Init CountryDD.Attributes.Add("onChange", "return onSelectionChanged();")
End Sub
Notice the zipcoderow appears and the focus is in it. The City and State rows remain hidden for now.
Step2: Handeling zip code entry.
Once the user enters the zip code and selects the first address field the java function will fire by using the onBlur.
The first function check if there is a value in the zipcodetb, if not it alerts the user and sets focus back on the zipcodetb, once it has a value it sends the request to the CityStateSearch.js.
The second function recieves the returned results and applies the values to the Citytb and Statetb, then makes them visible.
Notice I show a sample of the info returned in the comments, as you can see it also includes latitude and logitude coordinants, so you can use this info to find the city on a map. Keep in mind that GeoNames offers a variety of Webservices for all sorts of uses, mapping being a good one.
//If using VS2008 change all getElementId's to use ('<%= controlsname.ClientID %>') , pay close attention to capitalization, Javascript sees ZipCode and zipcode differently and will throw an error.
<script type="text/javascript">
function processrequest() { var zipcoderequest = document.getElementById('ZipCodetb').value; var countryrequest = document.getElementById('CountryDD').value; if (zipcoderequest == "") { alert("Zip code must be entered."); document.all('ZipCodetb').focus();
}
else {
callCityStateSearch(zipcoderequest, countryrequest);
}
}
function handleResult(result) { for (var property in result.postalcodes) {
// returned values {"postalcodes":{"postalcode":"04401","adminCode1":"ME","countryCode":"US","lng":-68.791839,"placeName":"Bangor","lat":44.824199,"adminName1":"Maine"}}
if (property == "placeName") { document.getElementById("Citytb").value = result.postalcodes[property];
}
if (property == "adminName1") { document.getElementById("Statetb").value = result.postalcodes[property];
}
}
document.getElementById("cityrow").style.visibility = "visible"; document.getElementById("staterow").style.visibility = "visible";
}
</script
Thats it. It should be up and running, this is a basic example and can be improved upon in many ways.
- Detect the country and reformat the fields for that countries mailing requirements.
- Do a reverse lookup by using city state to find zip
But for now thats it, I hope this helps you get started and if I get a chance I'll share again.
Enjoy
Here is a download for VB and C# using VS2008 AutoFillAddress.rar (11.48 kb)