Wednesday, September 9, 2009

jQuery AutoComplete in ASP.NET MVC Framework

Goal:

jQuery AutoComplete in ASP.NET MVC Framework with callback to customize result


Platform/Environment:

  • Asp.Net Mvc version 1
  • jQuery 1.3.2
  • Autocomplete - jQuery plugin 1.0.2

Quick Solution:


Here I am simply returning a list of account names:

$('#Name').autocomplete(
$('#ajaxListMatchingAccountNamesUrl').val(), { delay: 10, minChars: 3, matchSubset: 1, matchContains: 1, cacheLength: 10, autoFill: true, mustMatch: false, selectFirst: true, max: 15 }
);

ajaxListMatchingAccountNamesUrl - this is a hidden input control in my master page that contains the url that I set as a hidden input field so as to allow the Mvc framework to set it dynamically and accessed client-side.

Here is the C# controller action:

[AcceptVerbs(HttpVerbs.Get)]
public string GetMatchingAccounts(string q)
{
if (String.IsNullOrEmpty(q))
return null;

var sb = new StringBuilder();
foreach (AccountNameIdDTO dto in _accountRepository.SearchForAccountsStartingWithNameSuffixKey(q))
{
sb.AppendLine(dto.Name);
}
return sb.ToString();
}

Complex Solution:

A situation where my account names may be the same for different acounts. In this scenario I let the server-side action append the unique ID to the name, surrounded by brackets: My Account Name (23). This way when the user selects the account, I store the name in the textbox and the ID is parsed out and stored in a protected/readonly textbox.

var $accountMaintenanceWrapper = $('div#AccountMaintenanceWrapper');
$accountMaintenanceWrapper.find("#MasterAccount").autocomplete(
$('#ajaxListMatchingAccountNamesWithIdSuffixUrl').val(),
{
delay:10,
minChars:3,
matchSubset:1,
matchContains:1,
cacheLength:10,
autoFill:true,
mustMatch: false,
selectFirst : true,
max: 15,
formatResult: function(row, i, total) //this is the actual value that will be placed inside the textbox
{
//Parse out the ID suffix in brackets and place only the account name itself into the textbox for the name
$accountMaintenanceWrapper.find('#MasterAccountId').val(i.substring(i.indexOf('(')+1,i.indexOf(')')));
return i.substring(0,i.indexOf('('));
}
}
);

$accountMaintenanceWrapper.find("#MasterAccount").result(SetupCallBackForMasterAccount).next().click(function() {
$(this).prev().search();
});

//When the auto complete textbox is empty, delete the key code from the text
$accountMaintenanceWrapper.find("#MasterAccount").bind('keyup',function() {
if ($(this).val() == '')
{
$accountMaintenanceWrapper.find('#MasterAccountId').val('')
}
});


//This function parses the matched auto complete value's suffix key (in between parenthesis)
function SetupCallBackForMasterAccount(event, data, formatted) {
$('#MasterAccountId').val( !data ? " " : formatted.substring(formatted.indexOf('(')+1,formatted.indexOf(')')));
}

Here is the C# code controller action:

[AcceptVerbs(HttpVerbs.Get)]
public string GetMatchingAgentsWithIdSuffix(string q)
{
if (String.IsNullOrEmpty(q))
return null;

var sb = new StringBuilder();
foreach (AccountNameIdDTO dto in _accountRepository.SearchForAccountsStartingWithNameSuffixKey(q))
{
if ((Repository.Get(dto.Id) as Agency) != null)
sb.AppendLine(dto.Name + "(" + dto.Id + ")");
}
return sb.ToString();
}


Couple of pointers:

  • $accountMaintenanceWrapper - I declare this variable as I am referencing this container several times and to speed performance let jQuery find it and set the reference only once.
  • MasterAccountId - this is the readonly textbox that will contain the value of the unique ID
  • MasterAccount - this is the textbox where the user enters the name of the account
  • minChars:3 - Starts the search after/on three characters
  • selectFirst : true - if the user does not click on the name and simply tabs out will select the first one

No comments:

Post a Comment