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 follows so as to allow the Mvc framework to set it dynamically:
<input id="ajaxListMatchingAccountNamesUrl" type="hidden" value="<%=Url.RouteUrl(new {controller = "Account", Action = "GetMatchingAccounts"})%>" />
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 $target = $('div#SomeWrapper');
$target.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
$target.find('#MasterAccountId').val(i.substring(i.indexOf('(')+1,i.indexOf(')')));
return i.substring(0,i.indexOf('('));
}
}
);
$target.find("#MasterAccount").result(SetupCallBackForMasterAccount).next().click(function() {
$(this).prev().search();
});
//When the auto complete textbox is empty, delete the key code from the text
$target.find("#MasterAccount").bind('keyup',function() {
if ($(this).val() == '')
{
$target.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<IAccount>.Get(dto.Id) as Agency) != null)
sb.AppendLine(dto.Name + "(" + dto.Id + ")");
}
return sb.ToString();
}
Couple of pointers:
- $target- 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. You can of course chnage this to anything that fits your needs or simply ignore it and directly target the textbox of interest (MaterAccountId in this example).
- 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