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

AJAX Client Controls

4.39/5 (9 votes)
24 Jul 2007CPOL6 min read 1   548  
This article explains how to create client controls with the ASP.NET AJAX Library.

Introduction

This article explains the development of client controls using the new AJAX framework. I have attached some sample client controls which are developed using the AJAX library. This article could be a good starting point for learning about AJAX client components development.

Background

JavaScript is an Object Oriented Language, but the approach is very different for other languages like C# or C++. JavaScript doesn't have a concept of classes, so objects are not instances of classes. JavaScript objects can be considered as a collection of name and value pairs.

Objects can be created in different ways in JavaScript. For example:

JavaScript
var table = new Object();
//or
var table = {};

Properties can be added to this object:

JavaScript
table.Name = "Ikea Comp table";
//or
table["Name"] = "Ikea Comp table";

If a property has not been defined before, then it will be created during assignment.

Similarly, functions can be added to the above table object:

JavaScript
table.GetName = function()
{
  return this.Name;
}

Another way to create objects in JavaScript is:

JavaScript
//this function can be represented 
//as a class with properties and functions inside it.

function Cat()
{
    this.name = "kitty"
    this.type = "big";
    this.Details = function ()
    {
        alert(this.name +" " +this.type);
    }
}

//This method is shared within all objects
//Prototype is used to define the template of the object and used 
//to implement inheritance. Reference to objects and arrays added to 
//the prototype object are shared between all the instances. 
//functions defined in prototype are also shared between objects and it 
//saves a little memory but are a little slow to call as compared to 
//functions defined in constructor
//another way to write Cat.prototype.speak = function()....

Cat.prototype={
    speak : function()
    {
    alert('meaaoooww');
    }
}

//Static Function on Cat Object

Cat.StaicFunc = function()
{
    //this.name or any other property is not accessible 
    //because it is a static method

    alert("static function called");
}

//object of class;

var cat = new Cat();    
cat.speak();
cat.Details();
Cat.StaicFunc();
    
Inheitance can be implemented in JavaScript as

function WildCat()
{
    Cat.call(this); //Calling the base constructor
}

WildCat.prototype  = new Cat();
//Overriding the base function speak

WildCat.proptotype.speak = function()
{
    alert('this is wild cat..Beware');
}

A quicker way to create an object is:

JavaScript
var team = {name:'', members:[], count:function() { return members.length }}

The above object is created using the JSON format. To learn more about the JSON format, "Google" for it :-).

JavaScript in the AJAX Library

The ASP.NET AJAX library extends the JavaScript type system to simulate object oriented constructs that are not currently available in JavaScript like interfaces, classes, and enumerations. Classes are created in a similar fashion as in JavaScript. To create a class in the AJAX framework:

C#
Type.registerNamespace('Sample');
Sample.Vehicle = function ()
{
    this._name;
    this._type;
}
Sample.Vehicle.prototype = {
   get_name : function()
   {
        return this._name;
   },
   set_name : function (value)
   {
        this._name = value;
   },
   get_type : function()
   {
        return this._type;
   },
   set_type : function(value)
   {
        this._type = value;
   },
   Honk :  function()
   {
        alert('Loud noise coming...cant you hear that');
   }     
}
Sample.Vehicle.registerClass('Sample.Vehicle');

In the above example, you just need to call registerNamespace and registerClass to register your namespaces and classes with the AJAX Library. The above class has two properties and a function. The get_ and set_ format should be used to get and set the values of properties.

The format for registerClass is something like:

C#
NameofClass.registerClass('NameofClass', BaseClass, InterfacesImplemented)

An interface can be defined as:

C#
Sample.IDrive = function()
{
    throw Error.notImplemented();
}
Sample.IDrive.prototype = {
Drive : function ()
    {
        throw Error.notImplemented();
    }
}
Sample.IDrive.registerInterface('Sample.IDrive');

To inherit a class from the above Vehicle class and implement the interface

C#
Sample.Honda = function()
{
    Sample.Vehicle.initializeBase(this);
    //if base class takes arguments in constructor 
    //then they can also be passed

    //Sample.Vehicle.initializeBase(this, [arg1,arg2,...]);

}
Sample.Honda.prototype = {
    Honk: function()
    {
        alert(this._name + ' honking ...');
    //To call base method

        //Sample.Honda.callBaseMethod(this, 'Test',[arg1,arg2,…]);

    } ,
    Drive : function()
    {
        alert('Driving...');
    }
}
Sample.Honda.registerClass('Sample.Honda',Sample.Vehicle, Sample.IDrive);

To add/remove an event, use add_eventname/remove_eventname. For example:

C#
Sample.List = function() {
    this._items = [];
    this._events = new Sys.EventHandlerList(); 
}
Sample.List.prototype = {
    add : function(item) {
        this._items.push(item);
        this._raiseEvent('itemAdded', Sys.EventArgs.Empty);
    },
    get_events : function() {
        return this._events;
    },
    add_itemAdded : function(handler) {
        this.get_events().addHandler('itemAdded', handler);
    },
    remove_itemAdded : function(handler) {
        this.get_events().removeHandler('itemAdded', handler); 
    },
    _raiseEvent : function(eventName, eventArgs) {
        var handler = this.get_events().getHandler(eventName); 
        if (handler) {
        if (!eventArgs) {
            eventArgs = Sys.EventArgs.Empty;
            }
            handler(this, eventArgs);
        }
    }
}
Sample.List.registerClass('Sample.List');

Creating Client Components with the AJAX Toolkit

To start with AJAX, install the ASP.NET AJAX toolkit, and create a website with the template "ASP.NET AJAX Enabled Website".

In every AJAX enabled website, there will be a ScriptManager control on the default page, and the 'Web.config' is configured for AJAX. The primary purpose of this ScriptManager control is to deliver all the AJAX script to the client and register the web services. The files this control deploys could be the scripts provided by the AJAX toolkit or any other script files created by you.

To deploy JavaScript files containing AJAX classes and code, write:

ASP.NET
<asp:ScriptManager ID="ScriptManager1" runat="server" >        
     <Scripts> 
          <asp:ScriptReference Path="~/TestObjects.js"  />
     </Scripts>
</asp:ScriptManager>

To add a reference to a Web Service:

ASP.NET
<asp:ScriptManager ID="ScriptManager1" runat="server" >        
        
<Services>
    <asp:ServiceReference Path="~/Services/Service1.asmx" />
    <asp:ServiceReference Path="~/Services/Service2.asmx" />
</Services>
</asp:ScriptManager>

By simply adding this control on the page, it can deliver the scripts declaratively or programmatically.

Background on Components

Components are the building blocks of any application. They encapsulate the functionality, and allow for reuse across projects. I will talk about the component model of the AJAX toolkit library, and will give an example. The AJAX Library allows creating components using JavaScript. The process of creating components using AJAX is similar to that of the .NET Framework. AJAX components derive from the Sys.Component class.

AJAX components are classified as:

  1. Visual Components: They have a user interface. They derive from the Behavior or the Control class. For example: Button, or behavior of a TextBox control when it gets focus (example provided with this article).
  2. Non Visual Components : They don't have any user interface. They derive from the Sys.Component class.

Image 1

The above figure shows the relationship between Component, Control, and Behavior. The difference between Control and Behavior is that Behavior is used to enhance the base functionality of the Control. For example : if a TextBox is assigned a new Behavior, then it will still accept input, but we can change the look of the TextBox when it gets focus. While, a Control is created to change the functionality of an existing control, for example: creating a new auto update textbox control.

Creating a Component

Creating a component requires you to override the Initialize and Dispose methods of the Sys.Component base class.

C#
Type.registerNamespace("Sample");

Sample.TestComponent = function() {
    Sample.TestComponent.initializeBase(this);
    }
    Sample.TestComponent.prototype = {
    //Overide initialize method

    initialize : function() {
        Sample.TestComponent.callBaseMethod(this, 'initialize');
        alert("Initialize called on TestComponent");
    },
    //Overide dispose method

    dispose : function() {
        alert("Dispose called on TestComponent");
        Sample.TestComponent.callBaseMethod(this, 'dispose');
    },
    time : function() {
        var a = new Date();        
        alert(a.getTime());
    }
}
Sample.TestComponent.registerClass('Sample.TestComponent',Sys.Component);

On the ASPX page, add:

C#
//register pageInit function to be called on initialized stage of the page

Sys.Application.add_init(pageInit);
function pageInit() {
       //creating component 

 //Alias of Sys.Component.create              

        $create(Sample.TestComponent, {'id':'testComponent1'});
}
function pageLoad() {
  //find component and call its methods and properties

  //$find -- Alias of Sys.Application.find

        var testComponent = $find('testComponent1');
        testComponent.time();
}

"Sys.Application" is a run time object provided by the library to manage client components registered in the application. "$create" is an alias for Sys.Component.create. This method performs many tasks behind the scenes to create a component. This method must be called during the init stage of the page. All the components created on the page using $create become child components of the Sys.Application object. $find is an alias for Sys.Application.find, and is used to get a reference of a child component of Sys.Application. $get is an alias for Sys.UI.DomElement.getElementById, and is used to get a reference of the DOM element.

The complete syntax for $create is:

C#
$create(ComponentName, {Dictionary of properties }, 
       { Dictionary of events}, {Dictionary of refrences}, 
         $get(associatedElementID));

For a component, we don't need associatedElementID, but for Behaviors and Controls, this is a required argument.

If the component is a Behavior or a Control, then $create attaches it to the element $get(associatedElementID).

The complete syntax for $find is:

C#
$find('componentId', container);

By default, all the components created by $create become child components of Sys.Application, so if we are creating some components using $create, then there is no need to specify the container in $find because the default search is under Sys.Application.

$create does the same work as done by the following lines of code:

JavaScript
var testComponent = new Sample.TestComponent();
            testComponent.set_id('testComponent');
            testComponent.initialize();
            Sys.Application.addComponent(testComponent);

It is not necessary to create components under Sys.Application, but it is a good practice. Components could be complex, and can contain other objects or child components, so a centralized location for initializing and disposing an instance is very important. When components are added in the container Sys.Application, then it takes care of initializing and disposing components. It makes sure that all components added are initialized before page load occurs.

We can also create the instance of the above component in the PageLoad event, as:

JavaScript
function pageLoad() {
    var testComponent = new Sample.TestComponent();
    testComponent.time();
}

Creating a Behaviour

A behavior derives from Sys.UI.Behaviour which inherits from Sys.Component. A behavior must be provided an element to be associated with. The constructor of a behavior takes an element, and the element is passed to the base class constructor.

For example:

JavaScript
Type.registerNamespace("Sample");

Sample.TestBehavior = function(element) {
    Sample.TestBehavior.initializeBase(this,[element]);
    }
    Sample.TestBehavior.prototype = {
    //Overide initialize method

    initialize : function() {
        Sample.TestBehavior.callBaseMethod(this, 'initialize');
        alert("Initialize called on TestComponent");
    },
    //Overide dispose method

    dispose : function() {
        alert("Dispose called on TestComponent");
        Sample.TestBehavior.callBaseMethod(this, 'dispose');
    }
}
Sample.TestBehavior.registerClass('Sample.TestBehavior',Sys.UI.Behavior);

The above behavior doesn't do anything. During a $create call, an element instance must be passed to which this behavior will be applied.

The $create will be like:

JavaScript
$create(BehaviorName, {Dictionary of properties }, 
       { Dictionary of events}, {Dictionary of refrences} , 
         $get(associatedElementID));

The $find will look like:

JavaScript
var instance = $find('ElementName$BehaviorName');

Creating a Control

Unlike a Behavior, a Control derives from Sys.UI.Control which inherits from Sys.Component. The constructor of a control also takes an element to be associated with. An element can only have one control associated with it.

For example:

C#
Type.registerNamespace("Sample");

Sample.TestControl = function(element) {
    Sample.TestControl.initializeBase(this,[element]);
    }
    Sample.TestControl.prototype = {
    //Overide initialize method

    initialize : function() {
        Sample.TestControl.callBaseMethod(this, 'initialize');
        alert("Initialize called on TestComponent");
    },
    //Overide dispose method

    dispose : function() {
        alert("Dispose called on TestComponent");
        Sample.TestControl.callBaseMethod(this, 'dispose');
    }
}
Sample.TestControl.registerClass('Sample.TestControl',Sys.UI.Control);

The $create for Control is same as for Behavior. The $find is also the same as in the case of Behavior. We can access a control by passing the ID in $find. The ID of a control cannot be set programmatically as in the case of Behaviors. The Sys.UI.Control class automatically assigns an ID to the control, which is same as the ID of the associated element. So, to access a control by $find, pass the ID of the element.

AJAX-Enabled Server Controls

The controls I explained above are client controls. These controls need to created during the init stage of page life cycle. The process of creating controls is encapsulated by the $create statement, but we need to manually inject a $create statement in the page. If we can automate the addition of this $create statement, then we can have a server control. The AJAX Server framework provides script descriptors which can be used to inject this statement in the page code.

Image 2

ScriptComponentDescriptors can be used to create non-visual components, and similarly, ScriptBehaviorDescriptor and ScriptControlDescriptor can be used to create Behaviors and Controls.

In my next article, I will explain how AJAX enabled server controls work.

License

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