Tuesday, May 13, 2008

ASP.NET Ajax Toolkit - getting a key-value pair from the auto complete extender

Autocomplete Extender is a nice control, no doubt. And free. I didn't check earlier versions but current solution uses JSON web services and pretty fast.
Originally the Autocomplete Extender lacked very important functionality - ability to operate with key-value pairs. If you think about that, the Google search auto complete serves its purpose - the resulting string IS the value which is important for the postback, but it is scarcely the case for the 98% of non-search applications. We need control to mimic dropdown list - so gimme ya money let me keep the value for the entered text! Such a functionality was added some time ago but regretfully the feature was left undocumented.

You need to make few small changes to the original example: add JavaScript handling on the client and modify Web Service to return more sophisticated array. The Web Service method has to generate the list with the items in {"First":"<Name>,"Second":"<Value>"} format. E.g.

{"First":"Nissan Oakville","Second":"34AF44d2-1190-f489"} 
Method AutoCompleteExtender.CreateAutoCompleteItem(name,value) can help to create proper format.

Page looks like this:

<script type="text/javascript" language="javascript">
function GetCode(source, eventArgs )
{
document.getElementById("hCode").value = eventArgs.get_value();
}
</script>

Enter Manufacturer: <asp:TextBox ID="txtManufacturer" runat="server"/><input name="hCode" type="hidden"/>
<ajaxToolkit:AutoCompleteExtender ID="AutoCompleteExtender1" runat="server"
TargetControlID="txtManufacturer"
ServicePath="~/WebServices/AutoCompleteService.asmx"
ServiceMethod="GetCompletionList"
CompletionInterval="100"
CompletionSetCount="20"
MinimumPrefixLength="2"
OnClientItemSelected="GetCode"/>
And this is a full web service code:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class AutoCompleteService : System.Web.Services.WebService
{

[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] GetCompletionList(string prefixText, int count)
{
List<string> names = new List<string>();
using (SqlConnection conn = DbGateway.GetConnection())
{
SqlCommand cmd = new SqlCommand("GetManufacturersByPrefix", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@prefix", prefixText));
cmd.Parameters.Add(new SqlParameter("@count", count));
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
string item = AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem
(reader["ManufacturerName"].ToString(),
reader["ManufacturerId"].ToString());
names.Add(item);
}
reader.Close();
}
conn.Close();
}
return names.ToArray();
}
}
As a result if postback is required, the hCode element will hold the value, matching the text from txtManufacturers box and can be extracted from Request.Form collection.

P.S. Microsoft has to apply some serious ass-kicking to their Documentation Generation (or whatever) Department. Poor MSDN documentation and samples are unbearable and this tradition seems to infect the Microsoft-flavored open source community.

37 comments:

GB said...

This is great posting saved me some hours of searching.

Anonymous said...

excellent !!!

Michael Goldobin said...

Appreciate your comment. It is good to know that my post helps.

Unknown said...

Excellent work.. Thanks a lot...

Anonymous said...

nice example man, i really helped me.

Anonymous said...

that really helped

Anonymous said...

my problem is that , i have a web service which returns a company name and town. every company is associated with some id and i want that id to go into hcode rather than whole companyname,town,id. any help

Michael Goldobin said...

Sorry, I didn't really understand the problem. Do you want to show company and town and capture the company id?
The simplest way is to use this kind of pair:
{"First":"CompanyName (Town),"Second":"CompanyId"}
Next choice is investigate how JSON array is getting mapped to the Extender.
The ultimate solution is create your own Extender version, which can be flexed to show multiple columns - that's what Microsoft should do from the beginning.

Anonymous said...

Excellent post - thanks mate!

Anonymous said...

great posting! But what if the user tries to press enter on the textbox without selecting any item from the autocomplete extender panel. I tried this and it throws be a javscript error at run time "value is null or not an object". Cant we avoid this error?
-acvincent

Michael Goldobin said...

Great observation! My example is more a walkthrough than a real production code. I didn't look carefully but my guess is the JSON pair is null so reading element of that pair should produce this kind of exception.
Of course some kind of plumbing must be put around.
This confirms that developers should not trusted in testing their own code - they tend to follow happy paths :)

Unknown said...

FYI, the sample code uses "GetCompletionList" as the webmethod in the webservice, but "GetCompletionLists" as the webmethod for the extender declaration. Hung me up for a bit, so it might be good to change that to save others the time.

Josh

Ajay said...

Thanks for this helpful example. Can you please show an example for the javascript code for OnClientItemSelected. your example has really showed me some path to do it right way.

Ajay said...

Sorry. My mistake. I missed the javascript code that you have given. Thanks a ton. Its really great piece of code tat you have provided. It has really saved me some time. I will try improving it further and put it for someone like me needing such code.

Anonymous said...

This is a great post.

The logic is working fine in IE, but I could not make it work in Firefox or Chrome.

While debugging, I have figured out that I can see a auto-populate list without setting up a value for OnClientItemSelected property of AutoCompleteExtender. As soon as I set value to OnClientItemSelected (which is required to retrieve the selected item value), the auto-populate does not appear in non-IE browser.

Would you please post your comments? I will check your blog periodically.

Thank you for you help.

Unknown said...

Excellent post..but I do have a problem.. I am selecting an item from the list, it populates the ID in the hidden field, then I go back and enter the item which is not is the list.. it still keeps the ID in the hidden field. Is there any way to clear the hidden field if Idont select the item from the list?

Rahul Jain said...

Hello I am having 1 issue From Your
OnClientItemSelected Actually The Problem When I Enter in the textbox from my keyboard its works but when i click from the Mouse Its Crashes [ Crashing Means Javascript Error UnSpecified Error ] Please Help Me With That :?


Thanks

Rahul Jain said...

How To Achive The Same Via UserControl Cause i am getting Error When i Try To Set The Target in Calling page Of UserControl

[ Calling Page Means in Which Page My usercontrol is called ]

Michael Goldobin said...

sunil: that part may be just missing from the scenario, great catch
rahul: I didn't notice that behaviour before and was unable to reproduce it. What browser you're referring to?

Anonymous said...

Thank you so much for this info! Microsoft is really missing the point of open source. We cant use the tools without directions!

Unknown said...

This article illustrates some very help techniques. I've been looking for a reference just like this. Thanks for submitting.
Grady Christie

Anonymous said...

Saved me a ton of time. Awesome stuff.

Anonymous said...

But what if the user types-in the whole text without selecting it from textbox.
How can we get the value in this case ?

Michael Goldobin said...

Think about using auto complete extender as not just a "UI prettiness" but rather as a architectural decision. Allowing user to type a flexible text should be a well thought through. In my scenario user could type free text but some extra functionality was available for a "strongly-typed" entry, which iwas found by a paired ID. This combination is not suitable for a data entry, for example, when exact match is required. In this case you choice should be a more or less "ajaxi-smart" drop-down list, which does not allow free text.

Anonymous said...

Thank you so much for this info!
This is a great post.
nice example man, i really helped me.

Anonymous said...

Nice example.

In my webservice this kind of pair:
{"First":"ClientName ,
"Second":"ClientId"}

All is working , but i am not able to extract hCode values .
Can u please tell how to extract "ClientId" from textbox.
Thanks a ton.

Anonymous said...

Get stuff! I spent days trying to figure this out and come across this website and solved all my problems.

John Croson said...

This was a great example for me. I used this method in reverse to display a name in another control when the lookup completed. Very nice. Thanks!

dwejn14 said...

Well done, very useful !! :)

Anonymous said...

This post is fantastic.. I went through several other posts which mentioned updating the toolkit yada yada.. this post is extremely clear and conscise. Thanks!

abraham mathew said...

Really Great Job Dear
jscript runtime error is occuring while selecting Object doesn'tsupport this property or method

abraham mathew said...

Really Great Job Dear
jscript runtime error is occuring while selecting Object doesn'tsupport this property or method

mathew abraham said...

Really Great Job Dear
jscript runtime error is occuring while selecting Object doesn'tsupport this property or method

Unknown said...

Sir nice article.
Can u tell me how to make this same for multiple column,
ie(ManufacturerName,ManufacturerID),(LocationName,LocationID)

Michael Goldobin said...

Maybe I didn't understand the question but it is the case I am talking bout - just substitute ID with Second column.

Anonymous said...

Thanks for the example. It saved me hours. I can't agree more about the poor documentation with the ajax toolkit.

Anonymous said...

awesome!!! thanks , appreciate your efforts.


© 2008-2013 Michael Goldobin. All rights reserved