Friday, December 2, 2011

Learning MVC: Adding Autocomplete Dropdown to the Input Element.

I added the autocomplete function to one of the views. Turned out to be a simple task if I use the jQuery-provided function. Initially I tried to use Dylan Verheul's plugin, but inevitably ended up with the "Object does not support this property or method" the reason for which I still did not find. Very frustrating. Anyway.

First I added the following line to the _Layout.shtml under Views/Shared

Next, I used the Named Sections to place the javascript that I want to be in my view. In the _Layout.shtml file I added the following line:

@RenderSection("JavaScript", required: false)

Now I can add the "JavaScript" section to any view in the following manner:

@section JavaScript
{
<script type="text/javascript">
$(function () {
$("#Title").autocomplete({
source: "/InBasket/Find",
minLength: 1,
select: function (event, ui) {
if (ui.item) {
$("#Title").val(ui.item.value);
}
}
});
});
</script>}

and the section will be added to the view when it's rendered. The script above binds the autocomplete function to the Title input field. The "/InBasket/Find" calls the Find method in the InBasketController (which I'll write a bit later). The minLength specifies how many characters have to be in the box before the autocomplete fires. If my database is large, I may set it to 3, 5 or more to avoid huge responses where everything starting with "a" is returned. But for now I just want to test the functionality, so I set it to "1". And then, when I select an item from the autocomplete list, it sets the value of my Title input box to this item.

So that's the View part, now the Controller part. I started in my repository and added a function to return all user's InBasket item titles that start with a particular string, and the results should not be case-sensitive.

//used by autocomplete: return all inbasket items where Title starts with a string provided
public IQueryable FindUserInBasketItemsByTitle(string title, int userID)
{
var inBaskets = db.InBaskets.Where(item => item.UserID == userID);
inBaskets = inBaskets.Where(item => item.Title.ToLower().StartsWith(title.ToLower()));
return inBaskets;
}

Next, I added the Find method to the View. The method gets the value from the Title input box and returns the JSON array of values. One thing to note: it is important that the string parameter is called "term". I didn't know that initially and was wondering for a while why my parameter "s" is always null. Here is the whole Find method:

public JsonResult Find(string term, Users user)
{
var inBaskets = repository.FindUserInBasketItemsByTitle(term, user.UserID);
var titles = new List();
foreach(InBasket inBasket in inBaskets)
{
titles.Add(inBasket.Title);
}
return Json(titles, JsonRequestBehavior.AllowGet);
}

And that's all - it only takes a dozen or two lines of code. The result is horribly ugly at the moment, but it's a proof of concept:

References:

Autocomplete dropdown with jQuery UI and MVC
ASP.NET MVC 3: Layouts and Sections with Razor
ASP.Net MVC 3 Razor: Include js file in Head tag by . Also posted on my website

No comments: