Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

Implementing Google like Suggestion using Autocomplete Extender

4.36/5 (12 votes)
24 Jun 2008CPOL4 min read 6   2.6K  
How to use autocomplete extender to provide contact suggestion

Introduction

This sample demonstrates how you can use the AutoCompleteExtender control from ASP.NET AJAX Control Toolkit and customize its behavior without modifying it.

Background

I like Google's suggest feature and was thinking of using it in any of my projects. ASP.NET's AJAX Control Toolkit has a similar control (AutoCompleteExtender) which provides the basic items to complete this functionality. I searched about its usage and found many examples, but I was not satisfied with them. They were all populating only a single field using this extender.

I then came up with an idea, why not fill the contact details including the name, email address, and phone numbers using just one extender, but without modifying any provided functionality, so that our code can be used with newer versions. Below is what it will look like after populating that contact form.

AutoSuggest

Using the Code

Let's check out how AutoCompleteExtender works. Below is the description for this on toolkit's sample site.

AutoComplete is an ASP.NET AJAX extender that can be attached to any TextBox control, and will associate that control with a popup panel to display words that begin with the prefix typed into the textbox.

The dropdown with candidate words supplied by a web service is positioned on the bottom left of the textbox.

It says that this control will fetch its data from a webservice, and it can be attached to a TextBox control (it can only be attached with one control). When users starts typing in that TextBox control, it fetches a suggestion list from the configured webservice.

So we need two things, one would be our sample contact page, and the other will b a webservice. Here is our webservice code.

C#
using System;
using System.Web;
using System.Collections;
using System.Collections.Generic;
using System.Web.Script;
using System.Web.Script.Serialization;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.Services.Protocols;
using AjaxControlToolkit;


/// <summary>
/// Summary description for SuggestionService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService()]
public class SuggestionService : System.Web.Services.WebService {

    public SuggestionService () {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }

    [WebMethod]
    [ScriptMethod()]
    public string[] GetContacts(string prefixText, int count, string contextKey) {
        List<string> items = new List<string>();
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        ContactManager manager = new ContactManager();
        List<contact> contacts = manager.GetContacts
				(int.Parse(contextKey), prefixText, count);
        foreach (Contact c in contacts)
        {
            items.Add(AutoCompleteExtender.CreateAutoCompleteItem
				(c.FullName, serializer.Serialize(c)));
        }

        return items.ToArray();
    }    
}

You will notice some extra adornments on service and web method. You must adorn this service with "ScriptService" attribute, because without it, you cannot access it from your client script.

C#
[ScriptService()]
public class SuggestionService : System.Web.Services.WebService {

Our webmethod would return a list of string items in JSON format, so it also requires ScriptMethod attribute on it.

C#
[WebMethod]
[ScriptMethod()]
public string[] GetContacts(string prefixText, int count, string contextKey) {}

In addition to that, you have also noticed that I have returned this data differently. I have used AutoCompleteExtender to create its own entries, because I want to return some extra data from my webservice, so that I can populate all related fields on my contact form. This feature is only available in the latest build of AJAX Control Toolkit (1.0.20229.0). It was not available before that. It provides a key-value pair, which is a very useful feature, if we want to display a list of friendly names against internal ids. CreateAutoCompleteItem method takes two argument, the first is the text which will be displayed and the second is the value which can be used by any custom script on the page.

C#
items.Add(AutoCompleteExtender.CreateAutoCompleteItem
			(c.FullName, serializer.Serialize(c)));

Now I had another obstacle to overcome, how to pass a complex data for a contact. Here comes JSON to my rescue. I serialized my contact objects using JavaScriptSerializer.

So far I have bragged that my contact object is a bit complex, here is what it looks like. Below is my Contact and Phone classes.

C#
/// <summary>
/// Summary description for Contact
/// </summary>
public class Contact
{
    protected int _contactId;
    public int ContactId
    {
        get { return _contactId; }
    }

    protected int _companyId;
    public int CompanyId
    {
        get { return _companyId; }
        set { _companyId = value; }
    }

    protected string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    protected string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    public string FullName
    {
        get { return _firstName + ' ' + _lastName; }
    }

    protected string _email;
    public string Email
    {
        get { return _email; }
        set { _email = value; }
    }
    public int _homePhoneId;
    public int _workPhoneId;

    protected Phone _homePhone;

    public Phone HomePhone
    {
        get { return _homePhone; }
        set { _homePhone = value; }
    }
    protected Phone _workPhone;

    public Phone WorkPhone
    {
        get { return _workPhone; }
        set { _workPhone = value; }
    }


    public Contact() : this(0, 0, 0)
    {
    }

    public Contact(int id) : this(id, 0, 0)
    {
    }

    public Contact(int id, int homephone, int workphone)
    {
        _contactId = id;
        _homePhoneId = homephone;
        _workPhoneId = workphone;
    }
}

/// <summary>
/// Summary description for Phone
/// </summary>
public class Phone
{
    protected int _phoneId;
    public int PhoneId
    {
        get { return _phoneId; }
    }

    protected string _areaCode;
    public string AreaCode
    {
        get { return _areaCode; }
        set { _areaCode = value; }
    }

    protected string _prefix;
    public string Prefix
    {
        get { return _prefix; }
        set { _prefix = value; }
    }

    protected string _number;
    public string Number
    {
        get { return _number; }
        set { _number = value; }
    }

    protected string _extension;
    public string Extension
    {
        get { return _extension; }
        set { _extension = value; }
    }

    public Phone() : this(0)
    {
    }

    public Phone(int id)
    {
        _phoneId = id;
    }
}

Now we have to work out on our contact page. Below is its basic HTML. This contact form contains firstname, lastname, email address, and phone number fields. Each phone number in-turn has a prefix, area code, number and extension fields.

HTML
<div style="border: 1px solid rgb(204, 204, 204); padding: 10px; width: 400px;">
    <table cellspacing="0" cellpadding="0" border="0">
        <tbody><tr>
            <td style="width: 100px; text-align: right;">First Name:</td>
            <td><asp:textbox runat="server" id="txtFirstName" /></td>
        </tr>
        <tr>
            <td style="width: 100px; text-align: right;">Last Name:</td>
            <td><asp:textbox runat="server" id="txtLastName" /></td>
        </tr>
        <tr>
            <td style="width: 100px; text-align: right;">Home Phone:</td>
            <td>
                <table cellspacing="0" cellpadding="0" border="0">
                    <tbody><tr>
                        <td><asp:textbox columns="3" 
				runat="server" id="txtHPAreaCode" /></td>
                        <td>-<asp:textbox columns="3" 
				runat="server" id="txtHPPrefix" /></td>
                        <td>-<asp:textbox columns="3" 
				runat="server" id="txtHPNumber" /></td>
                    </tr>
                </tbody></table>
            </td>
        </tr>
        <tr>
            <td style="width: 100px; text-align: right;">Work Phone:</td>
            <td>
                <table cellspacing="0" cellpadding="0" border="0">
                    <tbody><tr>
                        <td><asp:textbox columns="3" 
				runat="server" id="txtWPAreaCode" /></td>
                        <td>-<asp:textbox columns="3" 
				runat="server" id="txtWPPrefix" /></td>
                        <td>-<asp:textbox columns="3" 
				runat="server" id="txtWPNumber" /></td>
                        <td> x </td>
                        <td><asp:textbox columns="3" 
				runat="server" id="txtWPExtension" /></td>
                    </tr>
                </tbody></table>
            </td>
        </tr>
        <tr>
            <td style="width: 100px; text-align: right;">Email Address :</td>
            <td><asp:textbox runat="server" id="txtEmail" /></td>
        </tr>
    </tbody></table>
</div>

To use AutoCompleteExtender on this page, first place a ScriptManager control, and then place AutoCompleteExtender control and set its TargetControlId to firstname TextBox control. This will add some extra properties in firstname TextBox control. These properties are related to its attached extender, you can find their description on AutoCompleteExtender sample page. We must set some of them to activate its working.

First specify the ServicePath and set it to our webservice we just created and then set the ServiceMethod, which will return a list of suggestions. This would be our webmethod in our service. We could also use context key, which can help us in filtering our suggestions. In our case, I have used it to pass current company information, so that I can return a particular company's contacts.

This completes our basic setup. If you test this page now, you will get a suggestion list after typing two letters in the firstname field, and when you select any item from that list, only firstname field is set with the selected name. All other fields would remain blank.

Now we need to add our magic code, which will enable us to achieve our goal.

JavaScript
<script type="text/javascript">
    function OnContactSelected(source, eventArgs)
    {
        var results = eval('('  + eventArgs.get_value() + ')');
        $get('txtFirstName').value = results.FirstName;
        $get('txtLastName').value = results.LastName;
        if (results.HomePhone.AreaCode != null)
            $get('txtHPAreaCode').value = results.HomePhone.AreaCode;
        if (results.HomePhone.Prefix != null)
            $get('txtHPPrefix').value = results.HomePhone.Prefix;
        if (results.HomePhone.Number != null)
            $get('txtHPNumber').value = results.HomePhone.Number;
        if (results.WorkPhone.AreaCode != null)
            $get('txtWPAreaCode').value = results.WorkPhone.AreaCode;
        if (results.WorkPhone.Prefix != null)
            $get('txtWPPrefix').value = results.WorkPhone.Prefix;
        if (results.WorkPhone.Number != null)
            $get('txtWPNumber').value = results.WorkPhone.Number;
        if (results.WorkPhone.Number != null)
            $get('txtWPExtension').value = results.WorkPhone.Extension;
        if (results.Email != null)
            $get('txtEmail').value = results.Email;
    }
</script>

But before that, we need to add some client-side behavior to our extender. We need to set OnClientItemSelected attribute of our extender to the above JavaScript function. This function would parse the value attribute, which is a valid JSON data passed from our webservice, and set appropriate fields with this parsed data.

This concludes our sample. Now you can test it yourself.

History

  • 2008-06-24 - My first attempt on AJAX auto extensions

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)