Fickle Bits

You're doing it wrong.

jQuery Auto-Complete Text Box With ASP.NET MVC

clip_image004This is an excerpt from Chapter 13 in my upcoming book, ASP.NET MVC in Action.

_________________________________________________

These days it is not uncommon to have text boxes automatically suggest items based on what we type. The results are further filtered as we type to give us the option to simply select an available item with the mouse or keyboard. One of the first examples of this in the wild was Google Suggest.

clip_image002

Figure 13.1 Google Suggest filters options as you type

A rudimentary implementation of this would simply monitor key-presses and fire off ajax requests for each one. Of course this means that fast typist would trigger many requests, most of which would be immediately discarded for the next request coming in 5 milliseconds. A good implementation will take into account a typing delay and also provide keyboard/mouse support for selecting the items.

Luckily jQuery has an extensive list of plugins available. One such plugin is Dylan Verheul’s autocomplete.

Dylan Verheul’s autocomplete

You can download the autocomplete plugin at http://www.dyve.net/jquery/ along with a few others including googlemaps and listify.

The basic idea is you have a simple text box on your page. The jQuery plugin adds the necessary behavior to handle key press events and fire the appropriate Ajax requests off to a URL that will handle the request. The URL needs point to a controller action, and by convention the response is formatted in a special way so the plugin could handle the response.

Assume for our purposes that we wanted to filter US Cities in the text box. The first step is to add a controller, action, and view for displaying the UI for this example. Ensure that jquery (in this case jquery-1.2.6.js) and jquery.autcomplete.js are referenced at the top of the view (or master page).

1
2
<script type=<span class="str">"text/javascript"</span> src=<span class="str">"../../scripts/jquery-1.2.6.js"</span>></script>
<script type=<span class="str">"text/javascript"</span> src=<span class="str">"../../scripts/jquery.autocomplete.js"</span>></script>

Next, add the text box. In this example we will call it city.

1
<%= Html.TextBox(<span class="str">"city"</span>) %>

Package this up with a simple controller (Listing 13.1).

Listing 13.1 – a controller & action for displaying our test page

1
2
3
4
5
6
7
<span class="kwrd">public</span> <span class="kwrd">class</span> HomeController : Controller
{
<span class="kwrd">public</span> ActionResult Index()
{
<span class="kwrd">return</span> View();
}
}

clip_image004

Figure 13.2 – Our simple view with a text box.

Now we add a little Javascript to add the autocomplete behavior.

1
2
3
4
5
<script type=<span class="str">"text/javascript"</span>>
$(document).ready(<span class="kwrd">function</span>() {
$(<span class="str">"input#city"</span>).autocomplete(<span class="str">'<%= Url.Action("Find", "City") %>'</span>);
});
</script>

Place this in the <head> of the page. You can see that the URL for the autocomplete behavior is specified as Url.Action(“Find”, “City”). This will point to a Find() action on the CityController. We’ll need to write this controller & action next.

Local Data Mode

The autocomplete plugin can also filter local data structures. This is useful when you have a limited set of data and you want to minimize requests sent to the server. The autcomplete plugin in local mode is also much faster, since there is no Ajax request happening behind the scenes. The only downside is that you must render the entire array onto the view.

Listing 13.3 – An action to find cities from an autocomplete ajax request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span class="kwrd">public</span> <span class="kwrd">class</span> CityController : Controller
{
<span class="kwrd">private</span> <span class="kwrd">readonly</span> ICityRepository _repository;
<span class="kwrd">public</span> CityController()
{
<span class="rem">//load up a CSV file with the city data</span>
<span class="kwrd">string</span> csvPath = Server.MapPath(<span class="str">"~/App_Data/cities.csv"</span>);
<span class="rem">//the repository reads the csv file</span>
_repository = <span class="kwrd">new</span> CityRepository(csvPath); #2
}
<span class="rem">//this constructor allows our tests to pass in a fake/mock instance</span>
<span class="kwrd">public</span> CityController(ICityRepository repository) #3
{
_repository = repository;
}
<span class="rem">//the autocomplete request sends a parameter 'q' that contains the filter</span>
<span class="kwrd">public</span> ActionResult Find(<span class="kwrd">string</span> q) #4
{
<span class="kwrd">string</span>[] cities = _repository.FindCities(q);
<span class="rem">//return raw text, one result on each line</span>
<span class="kwrd">return</span> Content(<span class="kwrd">string</span>.Join(<span class="str">"\n"</span>, cities));
}
}

The details of the CityRepository can be found in the code samples provided with the book. For now, we will focus on the new Find(string q) action. Since this is a standard action, you can actually just navigate to it in your browser and test it out. Figure 13.3 shows a quick test.

clip_image006

Listing 13.3 – A simple HTTP GET for the action with a filter of “hou” yields the expected results.

Now that we are sure that the action is returning the correct results, we can test the textbox. The Javascript we added earlier hooks up to the keypress events on the textbox and should issue queries to the server. Figure 13.4 shows this in action.

clip_image008

Figure 13.4 – The results are display in a <ul> tag. We can apply CSS to make it look nicer.

The drop down selections are unformatted by default, which makes them a little ugly. A little CSS magic will make it look much nicer. Listing 13.4 shows some sample CSS for this.

Listing 13.4 – CSS used to style the autocomplete results

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<span class="kwrd"><</span><span class="html">style</span> <span class="attr">type</span><span class="kwrd">="text/css"</span><span class="kwrd">></span>
div.ac_results ul {
margin:0;
padding:0;
list-style-type:none;
border: solid 1px #ccc;
}
div.ac_results ul li {
font-family: Arial, Verdana, Sans-Serif;
font-size: 12px;
margin: 1px;
padding: 3px;
cursor: pointer;
}
div.ac_results ul li.ac_over {
background-color: #acf;
}
<span class="kwrd"></</span><span class="html">style</span><span class="kwrd">></span>

clip_image010

Figure 13.5 – The styled dropdown results look much nicer. The selected item is highlighted, and can be chosen with the keyboard or the mouse.

The auto-complete plug-in has many options for you to configure to your needs. For the simple case that we’ve shown here, it’s as simple as this:

1
$(your_textbox).autocomplete(<span class="str">'your/url/here'</span>);

Other options for the plugin are listed below:

inputClass This class will be added to the input box.
resultsClass default value: “ac_results”
loadingClass The class to apply to the input box while results are being fetched from the server. Default is “ac_loading.”
lineSeparator Default is \n
minChars The minimum # of characters before sending a request to the server. Default is 1.
delay The delay after typing when the request will be sent. Default is 400ms.

 

There are many more options, but these are some common ones. To set these options, you include them in a dictionary as the second argument to the autocomplete method like this:

1
2
3
4
$(<span class="str">"input#city"</span>).autocomplete(<span class="str">'<%= Url.Action("Find", "City") %>'</span>, {
minChars : 3,
delay : 300
});

This type of functionality is immensely useful for selecting from large lists. It keeps your initial page size down by not loading all of these items at once and is very user-friendly.

________________________________________

clip_image004This is an excerpt from Chapter 13 in my upcoming book, ASP.NET MVC in Action.

Comments