Home / AJAX

How Do I Create an Auto-Complete TextBox?

RSS
Modified on 2010/06/06 06:10 by Stephen Walther Categorized as Uncategorized

Problem

I'm creating a form in an ASP.NET MVC view. I want to display suggestions while a user types text into a text field in the form. How do I create an auto-complete TextBox in an MVC view?

Solution

You can use the jQuery UI Autocomplete widget to display suggestions while a user types text into a text field. You can retrieve the list of suggestions from a JavaScript array or you can retrieve the list of suggestions from an ASP.NET MVC controller action.

Using the Autocomplete Widget with a JavaScript Array

When possible, you should retrieve the list of suggestions from a JavaScript array because this approach results in the best performance. You should take this approach when your list of suggestions is small -- less than a few hundred suggestions.

A perfect example of when it makes sense to take this approach is a list of countries. There are around 200 countries in the world and this list of countries is relatively static.

The following listing demonstrates how you can create an auto-complete TextBox that displays suggestions for countries.

Click Try Me! to try this sample live. Enter the country France, Germany, Indonesia, or USA into the TextBox to see the suggestion.

Try Me!
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<jQueryDemos.Areas.Autocomplete.Models.Customer>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Create</title>
    <link href='<%: Url.Content("~/Content/redmond/jquery-ui-1.8.2.custom.css")%>' rel="stylesheet"
        type="text/css" />

</head> <body> <% using (Html.BeginForm()) {%> <%= Html.ValidationSummary(true) %>

<fieldset> <legend>Create Customer</legend> <div class="editor-label"> <%= Html.LabelFor(model => model.Name) %> </div> <div class="editor-field"> <%= Html.TextBoxFor(model => model.Name) %> <%= Html.ValidationMessageFor(model => model.Name) %> </div> <div class="editor-label"> <%= Html.LabelFor(model => model.Country) %> </div> <div class="editor-field"> <%= Html.TextBoxFor(model => model.Country) %> <%= Html.ValidationMessageFor(model => model.Country) %> </div> <p> <input type="submit" value="Create" /> </p> </fieldset>

<% } %>

</body> <script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-1.4.2.min.js") %>' type="text/javascript"></script> <script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-ui-1.8.2.custom.min.js") %>' type="text/javascript"></script> <script type="text/javascript">

var countries = ["France", "Germany", "Indonesia", "USA"];

$("#Country").autocomplete({ source: countries });

</script> </html>

The above listing contains an ASP.NET MVC view that contains a new customer form. The form has two TextBoxes for entering a customer name and country. When you start typing into the Country TextBox, a list of suggestions appear:

The country TextBox is created using the standard ASP.NET MVC Html.TextBox() helper method:



<%: Html.TextBoxFor(model => model.Country) %>


The jQuery UI Autocomplete widget is used to convert the TextBox into an Autocomplete TextBox. To use the Autocomplete widget, you must add both the jQuery and jQuery UI libraries to your page:

<script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-1.4.2.min.js") %>' type="text/javascript"></script>
<script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-ui-1.8.2.custom.min.js") %>'  type="text/javascript"></script>

After you add these two libraries, you can use the Autocomplete widget like this:



var countries = ["France", "Germany", "Indonesia", "USA"];

$("#Country").autocomplete({ source: countries });


The #Country selector is used to retrieve the input element rendered by the MVC Html.TextBoxFor() helper. The AutoComplete widget is applied to the TextBox. A static array of countries is assigned to the Autocomplete source property.

There is one more thing that you must do to get the Autocomplete widget to work. You must add the jQuery UI Cascading Style Sheet to the page:

<head>
    <title>Create</title>
    <link href='<%: Url.Content("~/Content/redmond/jquery-ui-1.8.2.custom.css")%>' rel="stylesheet"
        type="text/css" />
</head>

If you neglect to add the Style Sheet, then the Autocomplete suggestions appear as an ugly bulleted list.

Using the Autocomplete Widget with an ASP.NET MVC Controller Action

If you need to offer a large set of suggestions, more than a few hundred, then you should retrieve the suggestions from the server by making an Ajax call against an ASP.NET MVC controller action.

Imagine, for example, that you want to create a movie lookup form. You enter the title of a movie in a TextBox. When you submit the form containing the TextBox, the details for the matching movie is displayed.

To make the lookup form easier to use, you can use the jQuery UI Autocomplete widget with the form. You can use the Autocomplete widget to display movie title suggestions when you enter a movie title into the TextBox.

The following controller exposes three actions:

using System.Linq;
using System.Web.Mvc;
using jQueryDemos.Models;

namespace jQueryDemos.Controllers { public class MovieController : Controller { private IMovieRepository _repository;

public MovieController() : this(new MovieRepository()) { }

public MovieController(IMovieRepository repository) { _repository = repository; }

[HttpGet] public ActionResult Index() { return View(); }

[HttpPost] public ActionResult Index(int? movieId) { if (!movieId.HasValue) { return View(); }

return View(_repository.Get(movieId.Value)); }

public ActionResult List(string term) { var results = from m in _repository.Movies where m.Title.StartsWith(term) select new { label = m.Title, id = m.Id };

return Json(results.ToArray(), JsonRequestBehavior.AllowGet); }

} }


The controller above has two Index() actions. The first Index() action simply displays the Index view. The second Index action is only invoked by an HTTP Post operation that includes a movie ID. This second Index() action returns the details for the movie that matches the ID.

Finally, the List() action is invoked by the AutoComplete widget to get the auto-complete suggestions. This action retrieves a list of movie titles that start with the parameter passed to the List() action.

Notice that the List() action returns the results as JSON by calling the Json() method. Notice, furthermore, the AllowGet option passed to the Json() method. Because the Autocomplete widget performs an HTTP GET request and not an HTTP POST request, you need to pass this option.

By default, the ASP.NET MVC framework will not return JSON in response to a GET request for security reasons. If you are passing sensitive data in your auto-complete suggestions then you should read the following blog entry:

http://haacked.com/archive/2009/06/25/json-hijacking.aspx

The following listing contains the Index view returned by both of the Index() controller actions.

Click Try Me! to view the code sample. Enter the letter 's' to see a list of suggestions for movies that start with the letter 's'.

Try Me!
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<jQueryDemos.Areas.Autocomplete.Models.Movie>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Movie Lookup</title>
    <link href='<%: Url.Content("~/Content/redmond/jquery-ui-1.8.2.custom.css") %>' rel="stylesheet"
        type="text/css" />
</head>
<body>

<% using (Html.BeginForm()) {%>

<fieldset> <legend>Movie Lookup</legend> <%: Html.TextBox("Title") %> <%: Html.Hidden("movieID") %> <input type="submit" value="Find" /> </fieldset>

<% } %> <% if (Model != null) { %> <fieldset> <legend>Results</legend> <div> <label>Movie Id:</label> <%: Model.Id%> </div> <div> <label>Movie Title:</label> <%: Model.Title%> </div> <div> <label>Movie Director:</label> <%: Model.Director%> </div> </fieldset> <% } %> </body> <script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-1.4.2.min.js") %>' type="text/javascript"></script> <script src='<%: Url.Content("~/Scripts/jQueryUI/jquery-ui-1.8.2.custom.min.js") %>' type="text/javascript"></script> <script type="text/javascript">

$("#Title").autocomplete({ source: '<%: Url.Action("List") %>', select: function (event, ui) { $("#movieID").val(ui.item.id); } });

</script>

</html>

The movie lookup form has two fields (one hidden):

<%: Html.TextBox("Title") %>
<%: Html.Hidden("movieID") %>
<input type="submit" value="Find" />        


When you select a movie title, the hidden movieID field is updated with the primary key that corresponds to the selected movie.

Here's the JavaScript code that is used to display the movie title auto-complete suggestions and that is used to update the hidden field:

$("#Title").autocomplete({
    source: '<%: Url.Action("List") %>',
    select: function (event, ui) {
        $("#movieID").val(ui.item.id);
    }
});


The Autocomplete source property calls the Movie controller List() action to retrieve the list of movies. The Autocomplete widget passes a parameter named term that contains the text that a user has typed into the TextBox.

The Autocomplete select event is raised when you select an auto-complete suggestion. In the code above, the movieID hidden form field is updated with the primary key of the movie selected. When you submit the movie lookup form, the primary key of the selected movie is submitted.