AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1624&pId=-1
AJAX Client Side Programming
page
by Brian Mains
Feedback
Average Rating: 
Views (Total / Last 10 Days): 37673/ 55

Introduction

The ASP.NET AJAX Framework provides many mechanisms to embed rich-client functionality in ASP.NET applications.  This framework provides a client-side set of components that extend JavaScript by providing a series of components meant to aid integration and development of web services, control extenders, and more.  All of these objects fit within the patterns Microsoft set forth in regards to their approach of AJAX development.  In addition, the ASP.NET AJAX Framework contains a set of server-side components and base classes that you can leverage in your custom AJAX components.

I've heard people ask why it is needed.  After all, AJAX is regularly available without the need of a specialized framework.  But what this framework is attempting to do is make development of AJAX components easier and more managed.  They've include a client-side framework that mimics the ASP.NET framework, by including classes and class inheritance, interface definitions, type checking and reflection, and many other mechanisms used in ASP.NET.  We'll take a look at that in this article.

Application Namespaces/Objects/Lifecycle

Like the server-side of ASP.NET, the ASP.NET AJAX client library utilizes namespaces, classes, enumerations, and interfaces using the same constructs that JavaScript already utilizes.  All of this will be discussed later; however, it's important to know that the framework already includes a series of namespaces for you.  The two biggest ones you'll see are the Sys and Sys.UI namespaces.

One of the core objects used in the application is the Application object, in the Sys namespace.  This is a core object that is responsible for the client components within the application, as well as handling the page lifecycle events and the disposal of objects.

To tap into the load and unload event on the client-side of the page, use the following JavaScript:

Listing 1

var myLoadHandler = Function.createDelegate(this, this.myLoadCallback);
var myUnloadHandler = Function.createDelegate(this, this.myUnloadCallback);
Sys.Application.add_load(myLoadHandler);
Sys.Application.add_unload(myUnloadHandler);
function myLoadCallback() { }
function myUnloadCallback() { }

When the load event fires on the client side, the myLoadCallback method runs.  Another common usage of the Application class is to notify that a custom script has been loaded.  When looking at AJAX control extender scripts, you will see this at the bottom:

Listing 2

if (typeof(Sys) !== 'undefined')
  Sys.Application.notifyScriptLoaded();

This sends a notification to the ScriptManager that the script has been loaded.

Another useful object is the PageRequestManager.  This object manages partial-page postback lifecycle, arising from the use of UpdatePanels that occur within the page.  To get an instance of the PageRequestManager, simply call the static getInstance() method; do not construct an instance yourself:

Listing 3

var pageManager = Sys.WebForms.PageRequestManager.getInstance();

When a partial-page update triggers, the page manager fires a beginRequest method that you can tap into.  As a simple illustration, I used the following code:

Listing 4

<asp:UpdatePanel ID="upTest" runat="server">
      <ContentTemplate>
            <asp:Label ID="lblDate" runat="server" Text='<%= DateTime.Now.ToString() %>' />
            <asp:Button ID="btnUpdate" runat="server" Text="Update" />
      </ContentTemplate>
</asp:UpdatePanel>
<script language="javascript" type="text/javascript">
pageManager.add_beginRequest(myBeginRequestCallback);
function myBeginRequestCallback()
{
      alert('begin request');
}
</script>

Whenever the button was clicked to update, the "begin request" alert message appears in the web page.

Helper Methods

The ASP.NET AJAX client framework contains helpful shortcut methods.  Some of the methods are the $addHandler, $removeHandler, $clearHandlers, $get, and $find.  Each one of these performs some action.

$addHandler

The add handler method registers an event handler for a DOM element's event.  For instance, a button is either a hyperlink "a" tag, or an "input" tag with a type of "button."  The $addHandler registers an event handler for that DOM event, such as the JavaScript click event that fires when you click it.  To create a delegate and attach to the event, use the following approach:

Listing 5

var clickHandler = Function.createDelegate(this, this.clickCallback);
$addHandler(button, "click", this.clickHandler);

In the above method, clickCallback is the name of a method; now _clickHandler contains a reference to the clickCallback method.  In the second line, get_element() returns a reference to a DOM element.  The second parameter defines the method to attach to (leaving out the "on" prefix), and the third parameter specifies the delegate to use so that when the DOM element is clicked, the delegate method is called, which points to clickCallback.  Now the _clickHandler is linked to clickCallback method, which fires when the click occurs.

$addHandlers

This method is a helper method to register event handlers in bulk.  By providing a key/value pairing of event name/handler, this registers for many events at once.

Listing 6

//textbox is DOM element
$addHandlers(textbox, {"keyup":keyupHandler, "keydown":keydownHandler});

$clearHandlers

This method clears all of the handlers that have been established for a DOM element. It's a quick way to get rid of registered handlers quickly:

Listing 7

//textbox is DOM element
$clearHandlers(textbox);

$removeHandler

This method removes a specific handler for a DOM element.  It allows you to specify the event and delegate to remove.

Listing 8

//textbox is DOM element
$removeHandler(textbox, "click", clickHandler);

$get

The get method is a shortcut for document.getElementById.  It allows you to reference a Dom element through its ID.  What you have to remember is that when the page runs, this often cannot be referenced by a server control's ID directly, but its client ID.  The approach you often see is to embed the client ID through the following approach:

Listing 9

var textbox = $get("txt1");

$find

When a class is registered through the registerClass method, this creates a hierarchy that sits between the DOM element and any server-side components being registered as well (though classes don't all have server-side components, features like AJAX controls and control extenders often do).  This method returns a reference to the class JavaScript component.

Listing 10

Var element = $find('<%= MyControl.ClientID %>');
element.set_Text("Some Value");

This allows you to access the class object and use the helpful methods like get_Text and set_Text (as described next).

ASP.NET Client Framework Development

ASP.NET AJAX and the AJAX Control Toolkit make use of client-side code to improve the quality of ASP.NET controls.  With ASP.NET AJAX, the developer can create a piece of code that classes, etc. that extend the application's client-side library and add additional functionality and promote reuse.  In the case of control extenders, this piece of code sits between the extender DOM element and the server-side extender class.  I'll talk more about this at the end of the section. 

The ASP.NET AJAX framework doesn't add any new syntaxes or flavors to the JavaScript language, but uses the existing notations in JavaScript and extends upon them.  Some of the benefits they've added are client-side type checking, class, interface, and enumeration declarations, inheritance, and many other features.  As in the .NET Framework, ASP.NET AJAX client components use namespaces as well.  To create a namespace, use the following designation:

Listing 11

Type.registerNamespace("Nucleo");

This registers a namespace, which allows a creation of a fully-qualified name, shown in the next example.  One of the other major mechanisms is the creation and registration of a class, which has a specific layout.  Below is an example:

Listing 12

Nucleo.MyClass = function()
{
  this._text = null;
}

So far, the above example defines the constructor.  The constructor takes a reference to the underlying DOM element that the class represents (such as in developing a custom control extender, which is a separate topic).  However, the constructor can have whatever parameters it wants.  Note that it's recommended to place any private variables in the constructor, and not in the prototype, for a performance gain.  In addition, any delegates require the use of a private variable, described later.

Listing 13

Nucleo.MyClass.prototype = 
{
      get_Text : function()
      {
            return this._text;
      },
 
      set_Text : function(value)
      {
            this._text = value;
      },
 
      clearText : function()
      {
            this._text = null;
      }
}
Nucleo.MyClass.registerClass("Nucleo.MyClass", null, Nucleo.MyInterface);

Note that properties, methods, and constructors use functions, which is a mechanism defined already by the JavaScript framework; the ASP.NET AJAX Client Library doesn't change the syntax of JavaScript, but adds objects you can use to increase what you can do with your client components.

Note the registerClass statement; the second parameter defines the base class to inherit from (null in this case means there isn't any base class).  Some of the options are: Sys.UI.Component, Sys.UI.Control, Sys.UI.Behavior, and AjaxControlToolkit.BehaviorBase.  This list isn't complete, and it's possible to inherit from your own custom classes as well.

ASP.NET AJAX also makes use of enumerations, which are very helpful in defining a finite set of values.  Check out the following example:

Listing 14

Type.registerNamespace("Nucleo");
//Empty constructor; not needed
Nucleo.MyEnumeration = function() { }
Nucleo.MyEnumeration.prototype =
{
      Yes : 1,
      No : 2,
      MaybeSo : 4
}
Nucleo.MyEnumeration.registerEnum("Nucleo.MyEnumeration", false);

Note the true value for the second parameter of registerEnum; this specifies whether the enumeration defines the flags attribute, meaning that it can use a bitwise operator to reference multiple enumerations for the same variable, similar to what you can do in .NET.

So what is the purpose of all of this?  Well, in the case of control extenders and AJAX controls, the JavaScript creates a class that sits on top of the underlying DOM element.  A text box client control is extended through a behavior that attaches to the DOM element and its events, but exposes its own properties and methods to perform any necessary actions it needs to.  In addition, the server-side piece of the control or extender uses this JavaScript class to interact with it initially.

In the case of control extenders, it works well because the server-side piece uses base classes that output the core assignments of properties and auto-generates other useful code that the application needs. The base class hides some of the details so you don't have to worry about it, while you can define some of the functionality in the server-side.  I'll be writing articles on server-side extenders later.

Web Services

ASP.NET AJAX heavily uses web services to perform client-server communication.  There are multiple ways to create a web service that the client can connect with.  The one way I'll touch upon here is the System.Net.WebServiceProxy class.  This class is the proxy to make a call to a method on a web service.  Whether the web service succeeds or fails, the appropriate method call is made.

As a simple test, I defined a WebMethod inside of my page as such:

Listing 15

[WebMethod]
public static string GetOutput()
{
  return "Successful";
}

The following markup simply makes the call to the page method and supplies the label with the text value returned from the web service method, if no errors occurred.  Otherwise, if errors did occur, the text "Error" appears from the onFailure callback.

Listing 16

<script language="javascript" type="text/javascript">
      function pageLoad()
      {
            var wsProxy = new Sys.Net.WebServiceProxy();
            wsProxy.set_timeout(0);
            var label = $get("output");
            
            var request = wsProxy._invoke("http://localhost:61379/testloader.aspx", 
                  "GetOutput", false, {}, onSuccess, onFailure, label);
      }
      
      function onSuccess(result, userContext, methodName)
      {
            userContext.innerText = result;
      }
      
      function onFailure(result, userContext, methodName)
      {
            userContext.innerText = "Error";
      }
</script>
 
<span id="output"></span>

Running this page makes the call to the web service and returns the result to the label.  A nice feature of Visual Studio 2008 is that you can debug both the server-side and client-side call to ensure it works correctly.

Type Inspection

Because there are methods to register namespaces, classes, interfaces, and enumerations, there also exists the ability to perform reflection similar to the System.Reflection namespace in .NET.  I used the following code to create a string message, checking whether specific objects were of the specified type.  I'll explain each below:

Listing 17

var classInstance = new Nucleo.MyClass();
var msg = "isNamespace(Nucleo) = " + Type.isNamespace(Nucleo);
msg += "\nisClass(Nucleo.MyClass) = " + Type.isClass(Nucleo.MyClass);
msg += "\nisClass(classInstance) = " + Type.isClass(classInstance);
msg += "\nisInterface(Nucleo.MyInterface) = " + Type.isInterface(Nucleo.MyInterface);
msg += "\nisInterface(classInstance) = " + Type.isInterface(classInstance);
msg += "\nimplementsInterface(Nucleo.MyClass) = " +    
  Type.implementsInterface(Nucleo.MyClass);
alert(msg);

Each of these checks whether the object is of a specified type, by using the Type.is<Type> methods.  However, these methods do not work for class instances, but rather inspect the actual type name itself.  In my methods above, anywhere classInstance is used, a false value was returned.

Exceptions

The client framework supports throwing exceptions through the use of exception classes.  The Error class has a series of methods that can be thrown.  For instance, if a parameter of a method is null, the following exception can be thrown:

Listing 18

throw new Error.argumentNull("value", "The value was null");

It's also possible to create custom exceptions; after all, an exception is a class object.  Many exception objects already exist in the framework, and so you have some options.  In case that doesn't suite you, it's possible to create your own and expose them from the Error class.  How to do that is out of scope for the article.

Conclusion

I've touched upon the basics about client programming and what you can do with it.  I plan to go into more detail about some of the more advanced topics in my next article.



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 8:54:58 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search