For once the Microsoft solution is not the trouble, but the JQuery is. To cut the story short, here is what I wanted to do:
- a textbox allowing a user to enter a post code
- use this post code to locate matching address from the database
- display the address NOT the post code on the drop down list.
- when an address selected, complete the post code and the address on the form.
[HttpPost]The Json() function will nicely serialize the object in JSON format for you. To prove this work, I use IE9's developer tool to check the response body
public ActionResult GetAddresses(string term)
{
AddressEntity _en = new AddressEntity ();
var addresses = _en.Addresses
.Where(p => p.PostCode.Contains(term))
.OrderBy(o => o.StreetName)
.ToList();
// .net is quite happy to serialize an object to
// json representation.
return Json(addresses);
}
Now, the tricky part. There are so many different tutorials on this very subject, and some are a bit out-dated. For example, to use the Autocomplete, you do not need to use the plug-in anymore, but to include the JQuery UI library as it is part of the standard UI library.
Anyway, here are things that works for me.
Firstly, include the following library and styles in the head section of your HTML page
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/ui-lightness/jquery-ui.css" type="text/css" media="all">
Next, create a Textbox and give it a unique ID
// this will generate HTML equivalent
// <input id="postTags" type="text">
<%: Html.TextBox("postTags") %>
Lastly, include the script similar to below:
$("#postTags").autocomplete(
{
source: function (request, response) {
$.ajax({
url: '<%=Url.Action("GetAddresses", "Home") %>',
type: "POST",
dataType: "json",
data: {
term: request.term
},
success: function (data) {
response($.map(data, function (item) {
// remap the object to the structed used by Autocomplete
return { label: item.FullOfficialName, value: item, id: item.ID }
}))
}
}); // end .ajax
},
minLength: 2,
select: function (event, ui) {
$("#Street").val(ui.item.value.StreetName);
$("#postTags").val(ui.item.value.PostCode);
// according to the documentation, cancelling the event, i.e. return false
// will prevent the default replacement action.
return false;
}
});
Here are some explanations.
In this example, the source is from an external source, and is obtained through AJAX. The source: function (request, response) specifies where the source is.
For the source option, you may notice the URL is generated dynamically using
<%=Url.Action("GetAddresses", "Home") %>Effectively, it generate a URL similar to this "/Home/GetAddresses"
Another important option is the dataType. It is important to set it to json, so the response will get handled correctly.
Now, the most important part. It seems the array used by the Autocomplete is an array of object which has 3 fields, label, value, and id. Since our object may carry more (or less) information, we need to do some transformations, hence the function:
success: function (data) {The purpose of this function is to use the map function to transform the objects in the array retrieved from the server to the objects with the format used by Autocomplete array.
response($.map(data, function (item) {
// remap the object to the structed used by Autocomplete
return { label: item.StreetName, value: item, id: item.ID }
}))
}
The label field is used by Autocomplete for the text displaying to the user, the value is the value used when an option is selected and finally, the id is the ID of the option.
As stated before, in my situation, I need all the information from the server, therefore I decided to set the value as the object itself. However, this causes a problem. When an option is selected, the value inserted to the textfield is [object] rather than the post code I wanted because the system do no know how to render my object.
In order to overcome this, I customize the select event handler as following
select: function (event, ui) {This handler is invoked when a selection is made, and it will retrieve and use the correct fields in my object for various part of my form. One important note, at the end of this function, you need to return false to stop the default behavior, otherwise, the #postTags will still be shown as [object] as before.
$("#Street").val(ui.item.value.StreetName);
$("#postTags").val(ui.item.value.PostCode);
// according to the documentation, cancelling the event, i.e. return false
// will prevent the default replacement action.
return false;
}