Centralized AJAX Caller

JQuery provides number of ways to make AJAX calls and creating a centralized helper class to call the server methods and capture the returning success or failure messages, can really help to speed up the development process. In this post, I will walk through a sample code, which can be easily used in any ASP.Net project to make server calls. By no means this is a bullet proof framework, there are many possible enhancements in it but it will provide enough guidance that anyone can use it to start working on their project.

I will be using the JQuery and some of it’s plugins, in this post. JQuery plugins will really help to perform UI tasks, which may not be directly related to AJAX calling but will provide a mechanism for making AJAX calls a better user experience.

One of the plugin is “block.ui”, it will be used to block the UI during the AJAX call, if we want user to wait and show an AJAX Loader. The same plugin has a feature to just show the AJAX loader, without blocking the UI. I will also show some code for JQuey UI tools, which will be really helpful to show the modal message dialogs, for errors or other application messages.

Let’s walk through the code sample.

1.

We need to create a separate class, to handle all the AJAX call. One option is to use the Javascript prototyping. We can then use this class in our asp.net application, to call the MVC controller actions.

In the constructor, we need few variables to control the overall behavior of our AJAX call. Since we want to show an AJAX loader so adding a “delay” timer will make sure that the AJAX loader won’t show up, unless call is taking more time than specific minimum time (e.g. we are using 1 second in the code here and loader won’t show if some call executes within a second).

function AJAX()
{
    var objSelf = this;

    // This struct will cache the current XmlHTTP requests
    // so that we can reference them if a call fails.
    this.currentRequests = {};
    
    //keeping the counter of active ajax calls. It will be used to hide or show the AJAX Loader
    this.activeAjaxCounter = 0;

    //default wait timer
    this.delayShowLoader = 1000;
}

2.
Some setup properties of the AJAX class will be helpful in appearance and data return. “UIHelper” is an example class, which can be created in the project to handle some common UI tasks, for instance, showing the message dialog or the ajax loader etc. “SourceElement” can be quite useful in situations where after the AJAX call succes, we need to make some changes on the control which has initiated the AJAX request.

/// <summary>Set 'true', if View needs to be disabled during AJAX call.</summary>
AJAX.prototype.disableView = true;

/// <summary>UIHelper to use from this AJAX.</summary>
AJAX.prototype.UIHelperRef = null;

/// <summary>Reference to the element, making the AJAX call.</summary>
AJAX.prototype.SourceElement = null;

3.
Creating a helper method inside the AJAX class will make sure that from calling code, we don’t need to provide the “Datatype” and “method” on ever call.

// This handles the JSON request. This checks to see if the current
// request is already being processed and also handles any error
// wiring that is required.
AJAX.prototype.PostJSON = function(requestName, requestURL, requestData, fnCallback)
{
    /// <summary>This function handles the JSON request. It will validate that a request with same name doesn't execute twice and
    /// it also handles any error wiring that is required.</summary>
    /// <param name="requestName">This is optional parameter. If "null" is provided for the value then multiple AJAX calls for the same request won't be blocked.</param>
    /// <param name="requestURL">Target URL for the AJAX call.</param>
    /// <param name="requestData">Required key/value pair data to pass to the target URL.</param>
    /// <param name="fnCallback">Call back function to handle the returned data and errors.</param>

    var objSelf = this;
    objSelf.Execute(requestName, requestURL, requestData, fnCallback, 'json', 'POST');
}

AJAX.prototype.PostHTML = function(requestName, requestURL, requestData, fnCallback)
{
    /// <summary>This function handles the HTML request. It will validate that a request with same name doesn't execute twice and
    /// it also handles any error wiring that is required.</summary>
    /// <param name="requestName">This is optional parameter. If "null" is provided for the value then multiple AJAX calls for the same request won't be blocked.</param>
    /// <param name="requestURL">Target URL for the AJAX call.</param>
    /// <param name="requestData">Required key/value pair data to pass to the target URL.</param>
    /// <param name="fnCallback">Call back function to handle the returned data and errors.</param>

    var objSelf = this;
    objSelf.Execute(requestName, requestURL, requestData, fnCallback, 'html', 'POST');
}

4.
This is the method which will do all the heavy lifting of making AJAX call and showing proper UI behavior. Code is well commented here to make it easy to follow the logic. AJAX loader delay is not 100% functional, as there can be situations where multiple calls can make it crash but in most of the situations it will work effectively.
In case of an AJAX call, we can take the advantage of returned JSON formatted data and create a structure on server to package the data with any server messages. For example, in a JSON post, the server can return an object with properties of “Data”, “Message” (success or new id etc) etc. For HTML ajax calls, we can simply grab the return HTML in callback function and manipulate it.

AJAX.prototype.Execute = function(requestName, requestURL, requestData, fnCallback, callDataType, callMethod)
{
    /// <summary>This method will be called internally by the AJAXHelper class.</summary>
    /// <param name="requestName">This is optional parameter. If "null" is provided for the value then multiple AJAX calls for the same request won't be blocked.</param>
    /// <param name="requestURL">Target URL for the AJAX call.</param>
    /// <param name="requestData">Required key/value pair data to pass to the target URL.</param>
    /// <param name="fnCallback">Call back function to handle the returned data and errors.</param>
    /// <param name="callDataType">Either "json" or "html".</param>
    /// <param name="callMethod">Either "GET" or "POST".</param>

    var objSelf = this;

    // First, we have to check to see if this request is
    // already being processed. We don't want the user to
    // try and fire off multiple requests of the same type.
    // Of course, if the name is NULL, then don't worry.
    if (!requestName || !this.currentRequests[requestName])
    {

        // Store current request.
        objSelf.currentRequests[requestName] = true;

        if (!objSelf.UIHelperRef)
            objSelf.UIHelperRef = new UIHelper();


	//if this is the first call then we need to initiate the AJAX loader. On subsequent call, we can skip this block, because the loader will already be shown for previous call.
        if (objSelf.activeAjaxCounter == 0)
        {
            setTimeout(
                function()
                {
		    //This check will make sure that once the 1 second delay is over, then still there is at least one active call which needs the AJAX loader.
                    if (objSelf.activeAjaxCounter > 0)
                        objSelf.UIHelperRef.ShowAJAXLoader(objSelf.disableView)
                }, objSelf.delayShowLoader);
        }

        //counter increment
        objSelf.activeAjaxCounter++;

        // Make actual AJAX request.
        $.ajax(
		    {
		        // Basic properties.
		        url: requestURL,
		        data: requestData,
		        dataType: callDataType,
		        type: callMethod,

		        // The success call back.
		        success: function(objResponse)
		        {
		            // Remove request flag.
		            objSelf.currentRequests[requestName] = false;

		            // Pass off to success handler.
		            if (callDataType == 'json')
		            {
		                objSelf.AJAXSuccessHandler(objSelf, objResponse, fnCallback);
		            }
		            else
		                fnCallback(objResponse);

		            //counter decrement
		            objSelf.activeAjaxCounter--;

		            //unblock the UI
		            if (objSelf.activeAjaxCounter == 0)
		                objSelf.UIHelperRef.HideAJAXLoader();
		        },

		        // The error handler.
		        error: function(objResponse, textStatus)
		        {
		            // Remove request flag.
		            objSelf.currentRequests[requestName] = false;

		            //counter decrement
		            objSelf.activeAjaxCounter--;

		            //unblock the UI
		            if (objSelf.activeAjaxCounter == 0)
		                objSelf.UIHelperRef.HideAJAXLoader();

		            // Pass off to fail handler.
		            objSelf.AJAXFailHandler(
		                objSelf,
					    objResponse,
					    textStatus,
					    fnCallback
					    );
		        }
		    }
		    );

    } else
    {
        // This request is currently being processed.
        alert("Request being processed. Be patient.");

    }
}

5.
Success handler is pretty simple but in case of an AJAX call failure, we should review the returned server error and display it to the user. We can even redirect to some specific error page if we dont’ want to display the message dialog.

// This will handle all AJAX failures.
AJAX.prototype.AJAXSuccessHandler = function(objAJAXCaller, objResponse, fnCallback)
{
    /// <summary>On the successful AJAX call, we have to update the Application messages section and execute the callback method. This method is internally called by the AJAXHelper.</summary>

    //************************
    //if returned data is packaging server messages then we can evaluate it here and show messages to the user in some modal popup or notification area	
    //************************


    //execute the callback method
    fnCallback(objResponse.ResultData, objAJAXCaller.SourceElement);
}

// This will handle all AJAX failures.
AJAX.prototype.AJAXFailHandler = function(objAJAXCaller, objResponse, textStatus, fnCallback)
{
    /// <summary>Since this AJAX request failed, let's call the callback but manually create a failure response. This is to handle the exceptions. All validation or business logic errors will be handled in the "success" call back of AJAX.</summary>

    //display the error dialog
    objAJAXCaller.UIHelperRef.CreateAJAXResponseErrorMessage( objResponse.responseText );

    fnCallback(
		{
		    IsSuccessful: false,
		    ResultData: ""
		});
}

6.
Using the “blockUI” can be quite helpful here as we can easily show the AJAX loader to the user. We can create some methods which will show and hide the loader on the UI. In the example code, I am using a .gif file for loader animation, which is set as the message to the blockUI.

UIHelper.prototype.ShowAJAXLoader = function(disableView)
{
    /// <summary>Displays the loader progress image at top right corner of the browser window.</summary>
    /// <param name="disableView">"true", if view needs to be disabled.</param>
    var objSelf = this;

    //this check will make sure that we don't make unnecessary calls to unblockUI method
    objSelf.ajaxLoaderShown = true;

    $.blockUI.defaults.css = {};
    $.blockUI({
        message: $('img#ajaxLoading'),
        centerY: 0,
        fadeIn: 100,
        showOverlay: disableView
    });
}

UIHelper.prototype.HideAJAXLoader = function()
{
    /// <summary>Hides the AJAX Loader image.</summary>

    var objSelf = this;

    if (objSelf.ajaxLoaderShown)
    {
        //unblock the UI but make the fadeout slower
        $.unblockUI({ fadeOut: 800 });
        objSelf.ajaxLoaderShown = false;
    }

}

7.
Following methods are really handy to show the “AJAX call failure”. In showMessageDialog, we are just using the JQuery UI Modal dialog, with the passed message parameter. We can create a blank div on a Master page and use it to hook the JQuery dialog. In this example code I am using the “messageDialogDiv” div.

//It will parse the response text to retrieve the "title" tag for the error information and display an error dialog.
UIHelper.prototype.CreateAJAXResponseErrorMessage = function( message )
{
    /// <summary>It will parse the response text to retrieve the "title" tag for the error information and display an error dialog.</summary>
    /// <param name="title">Title for the dialog.</param>
    /// <param name="message">Full response text from Response Object.</param>

    var errorMessage = '';
    message.replace(/[^<]*(<title>([^<]+)<\/title>)/gi, function()
    {
        errorMessage = arguments[2];
    });

    if (errorMessage.length == 0)
        errorMessage = 'Unspecified Error';

    this.ShowMessageDialog('Application Error', errorMessage);
}

//Displays a standard message dialog for errors or other notifications.
UIHelper.prototype.ShowMessageDialog = function(title, message)
{
    /// <summary>Displays a standard message dialog for errors or other notifications.</summary>
    /// <param name="title">Title for the dialog.</param>
    /// <param name="message">Actual HTML formatted or simple string message to show in the dialog.</param>

    var dialogDiv = $('#messageDialogDiv');

    if (title == null || title.length == 0)
        title = 'Application Message';

    dialogDiv.empty();
    dialogDiv.attr('title', title);
    dialogDiv.append(message);

    dialogDiv.dialog({
        width: 400,
        modal: true,
        buttons: {
            Ok: function()
            {
                $(this).dialog('close');
                $(this).dialog("destroy");
            }
        }
    });
}

8.
The empty div can simply be defined with display style set to none. JQuery dialog will handle it showing to the user and hiding it.

    <div id="messageDialogDiv" title="Error" style="display:none;">
    </div>
Advertisements

Tags: ,

About prres

I have been working with Microsoft Tools and Technologies for 9 years. Most of my work include architecting and developing with C#, ASP.Net, Silverlight and SQL Server.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: