Click here to Skip to main content
6,936,634 members and growing! (20,516 online)
Email Password   helpLost your password?
 
Web Development » Ajax and Atlas » Samples     Intermediate License: The Code Project Open License (CPOL)

AJAX Look Up Caching

By fly904

Cache server response to reduce server load.
C#, Javascript, .NET, ASP.NET, Ajax, VS2008, Dev
Revision:3 (See All)
Posted:22 Feb 2009
Views:5,974
Bookmarked:23 times
printPrint Friendly   add Share
      Discuss Discuss   Broken Article?Report  
4 votes for this article.
Popularity: 2.71 Rating: 4.50 out of 5

1

2

3
2 votes, 50.0%
4
2 votes, 50.0%
5

Introduction

This is an AJAX control which enables multiple instances of an auto-complete list using JQuery.

The project uses the Ajax.NET framework.

Background

After reading firefalcon's article (Implementing an Ajax.NET-based Lookup Server Control), I wanted to add several instances of it and also cache the information sent by the server.

Using the code

First of all, we need to implement the functions to be called from the client:

using System;
using System.Collections;
using System.Web;
using Ajax;

namespace AjaxExample
{
    public partial class Index : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //Set search to call the simple example
            this.search.Attributes.Add("onkeyup", 
              "Index.GetSearchItems(this.value, GetSearchItems_CallBack);");
            Utility.RegisterTypeForAjax(typeof(Index));

            //Add autocomplete off using javascript to keep valid XHTML 1.1
            Page.ClientScript.RegisterStartupScript(typeof(System.Web.UI.Page), 
              "SeachAutocomplete", "<script type=\"text/" + 
              "javaScript\">$('#search').attr('autocomplete', 'off')</script>");
        }

        [AjaxMethod()]
        public ArrayList GetSearchItems(string query)
        {
            // use real method to query data from database instead

            ArrayList items = GetRecords();

            ArrayList matchItems = new ArrayList();

            if (query != string.Empty)
            {
                foreach (string item in items)
                {
                    if (item.ToLower().StartsWith(query.ToLower()))
                        matchItems.Add(item);
                }
            }
            return matchItems;
        }

        [AjaxMethod()]
        public ArrayList GetRecords()
        {
            ArrayList items = new ArrayList();
            items.Add("Ted");
            items.Add("Teddy");
            items.Add("Mark");
            items.Add("Alfred");
            return items;
        }
    }
}

Now, we get onto the fun stuff. We create a custom ASPX control which will automatically call the server function:

public class AjaxLookupOnce : TextBox
{
    private string callBackFunction = "";
    private string backgroundColor = "#EEE";
    private string highlightColor = "#CCC";
    private string font = "Verdana";
    private string divPadding = "2px";
    private string divBorder = "1px solid #CCC";

    public string CallBackFunction
    {
        get { return callBackFunction; }
        set
        {
            callBackFunction = value;

            //Set the onkeyup attribute to call the function.
            this.Attributes.Add("onkeyup", 
                      "CheckAndShowArray(this);");
        }
    }

    public string BackgroundColor
    {
        get { return backgroundColor; }
        set { backgroundColor = value; }
    }

    public string HighlightColor
    {
        get { return highlightColor; }
        set { highlightColor = value; }
    }

    public string DivFont
    {
        get { return font; }
        set { font = value; }
    }

    public string DivPadding
    {
        get { return divPadding; }
        set { divPadding = value; }
    }

    public string DivBorder
    {
        get { return divBorder; }
        set { divBorder = value; }
    }
}

Notice that when the CallBackFuntion is set, the textbox will have an onkeyup attribute with what to do when a key has been pressed.

To initialize the textbox for an auto-complete div, we need to add the following JavaScript (jquery.js is needed for the following to work):

var current;
var cache = new Array();

//Create result div for the textbox and cache it in the array cache
function InitializeTextboxEvent(TextboxName, StyleArray)
{
    //add "autocomplete=off" and blur attributes to the textbox
    $('#' + TextboxName).blur(hideDiv).attr('autocomplete', 'off');

    //Cache the textbox id, result div id, 
    //result div style array and result array
    var index = cache.length;
    cache[index] = new Array(4);
    cache[index]['Textbox'] = '#' + TextboxName;
    cache[index]['ResultDiv'] = createDiv(TextboxName, StyleArray);
    cache[index]['StyleArray'] = StyleArray;
    cache[index]['ResultArray'] = null;

    current = cache[cache.length];
}

//Create result div
function createDiv(TextboxID, StyleArray)
{

    divID = TextboxID + '_result';
    if ($('#' + divID).length == 0) {
        $('body').append($('<div></div>').attr('id', divID));

        $('#' + divID).css({

            'background': StyleArray['DIV_BG_COLOR'],
            'font-family' : StyleArray['DIV_FONT'],
            'padding' : StyleArray['DIV_PADDING'],
            'border': StyleArray['DIV_BORDER'],
            'width': $('#' + TextboxID).width(),
            'font-size' : '90%',
            'padding' : '2px 2px 2px 2px',
            'position' : 'absolute',
            'left': $('#' + TextboxID).offset().left + 'px',
            'top': ($('#' + TextboxID).offset().top + $('#' + 
            TextboxID)[0].offsetHeight) + 'px',
            'visibility' : 'hidden',
            'z-index' : 10000

        });
    
    }
    return '#' + divID;
}

To call the InitializeTextboxEvent(), we need to do so from the new custom control we made earlier. So to that class, we add:

protected override void Render(HtmlTextWriter writer)
{
    if (callBackFunction != string.Empty)
    {
        base.Render(writer);

        //Set the drop down box style and Initialize the textbox
        string initialize = string.Format("<script type=\"text/javaScript\">" + 
                Environment.NewLine +
                "var Style = new Array(5);" + Environment.NewLine +
                "Style['DIV_BG_COLOR']  = '{0}';" + Environment.NewLine +
                "Style['DIV_HIGHLIGHT_COLOR'] = '{1}';" + Environment.NewLine +
                "Style['DIV_FONT'] = '{2}';" + Environment.NewLine +
                "Style['DIV_PADDING'] = '{3}';" + Environment.NewLine +
                "Style['DIV_BORDER'] = '{4}';" + Environment.NewLine +
                "InitializeTextboxEvent('{5}', Style);" + Environment.NewLine +
                callBackFunction + "(CacheArray);" + Environment.NewLine +
                "</script>", BackgroundColor, HighlightColor, 
                DivFont, DivPadding, DivBorder, this.ClientID);

        Page.ClientScript.RegisterStartupScript(typeof(Page), "Register_" + 
                                                this.ClientID, initialize);
    }
}

In this function, we add the style elements declared in the custom control, and call the InitializeTextboxEvent function with the ID of the control and the style array.

We also cache the callback function in the CacheArray function, as shown below:

function CacheArray(response)
{
    //Calls seperate function as 'this' variables are undefined in this function
    SetCurrentArray(response.value);
}
function SetCurrentArray(response)
{
    cache[cache.length - 1]['ResultArray'] = response;
}

Remember the onkeyup function we specified earlier when CallBackFunction was defined? Well now, it is time to create it.

//Check Value with cached results
function CheckAndShowArray(e)
{
    queryElement = e;
    if (!getCurrent())
        return false;
    var value = e.value;
    $(current['ResultDiv']).html('');
    if (trim(value).length > 0)
    {
        for (var i = 0; i < current['ResultArray'].length; i++)
        {
          if (trim(current['ResultArray'][i].toLowerCase()).startsWith(value.toLowerCase()))
              $(current['ResultDiv']).append($('<span></span>').attr('class', 
                 'results').html(current['ResultArray'][i])).append($('<br />'));
        }
    }
    addEvents($(current['ResultDiv']).html().length > 0);
}

//Return cache for the textbox queryElement
function getCurrent()
{
    var found = false;
    for (var i = 0; i < cache.length && !found; i++)
    {
        if ($(cache[i]['Textbox'])[0] == queryElement)
        {
            current = cache[i];
            found = true;
        }
    }
    return found;
}

//Hide/Show result div
function SetDivVisibility(show)
{
    if (getCurrent())
    {
        if (show)
            $(current['ResultDiv']).css({ 'visibility': 'visible' });
        else
            $(current['ResultDiv']).css({ 'visibility': 'hidden' });
    }
}
function hideDiv()
{
    SetDivVisibility(false);
}

//Add hover and click events to the results
function addEvents(show)
{
    $('.results').css({
        'font-weight': 'bold',
        'cursor': 'pointer',
        'padding': '2px'
    }).hover(
    function() {
        $(this).css({ 'background': current['StyleArray']['DIV_HIGHLIGHT_COLOR'] });
        $(current['Textbox']).unbind('blur');
    },
    function() {
        $(this).css({ 'background': current['StyleArray']['DIV_BG_COLOR'] });
        $(current['Textbox']).blur(hideDiv);
    }).click(
    function() {
        $(current['Textbox']).attr('value', $(this).text());
        hideDiv();
    });

    SetDivVisibility(show);
}

//Trim and starts with
function trim(str, chars) {
    return ltrim(rtrim(str, chars), chars);
}
function ltrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}
function rtrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}
String.prototype.startsWith = function(str) {
    return (this.match("^" + str) == str)
}

The code simply checks the cache for all words starting with the textbox value. To use the custom form, use:

<Custom:AjaxLookupOnce
        runat="server" 
        id="AjaxLookupOnce2" 
        BackgroundColor="#EEE" 
        DivBorder="1px solid #CCC" 
        DivPadding="2px"  
        DivFont="Arial" 
        HighlightColor="green" 
        CallBackFunction="Index.GetRecords" />

Now, let's look at the server response for this:

lightServer.jpg

As you can see, two keys have been typed and only one response has come back from the server on page load.

Now, let's compare it with a modified (enables multiple instances, use JQuery and onkeyup rather than onkeypress) version of firefalcon's article. This method calls the server each time a key is typed.

heavyServer.jpg

Two keys have been typed again, but this time it has two server responses taking 1 sec each, therefore at least a second's delay in the drop down.

Both versions and another simple example are available in the accompanying zip file.

Acknowledgement

Thanks to firefalcon for the original version which I have modified.

License

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

About the Author

fly904


Member
He is a student currently studying Computer Engineering at The University of Reading. After learning to program in Java, mostly self taught, he moved on to PHP and a little bit of web development. When he started university he quickly picked up C and C++. He has now moved onto C# as it's just easy.
Occupation: Software Developer
Location: United Kingdom United Kingdom

Other popular Ajax and Atlas articles:

 
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.

PermaLink | Privacy | Terms of Use
Last Updated: 22 Feb 2009
Editor: Smitha Vijayan
Copyright 2009 by fly904
Everything else Copyright © CodeProject, 1999-2010
Web20 | Advertise on the Code Project