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:
var table = new Object();
var table = {};
Properties can be added to this object:
table.Name = "Ikea Comp table";
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:
table.GetName = function()
{
return this.Name;
}
Another way to create objects in JavaScript is:
function Cat()
{
this.name = "kitty"
this.type = "big";
this.Details = function ()
{
alert(this.name +" " +this.type);
}
}
Cat.prototype={
speak : function()
{
alert('meaaoooww');
}
}
Cat.StaicFunc = function()
{
alert("static function called");
}
var cat = new Cat();
cat.speak();
cat.Details();
Cat.StaicFunc();
Inheitance can be implemented in JavaScript as
function WildCat()
{
Cat.call(this);
}
WildCat.prototype = new Cat();
WildCat.proptotype.speak = function()
{
alert('this is wild cat..Beware');
}
A quicker way to create an object is:
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:
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:
NameofClass.registerClass('NameofClass', BaseClass, InterfacesImplemented)
An interface can be defined as:
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
Sample.Honda = function()
{
Sample.Vehicle.initializeBase(this);
}
Sample.Honda.prototype = {
Honk: function()
{
alert(this._name + ' honking ...');
} ,
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:
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:ScriptManager ID="ScriptManager1" runat="server" >
<Scripts>
<asp:ScriptReference Path="~/TestObjects.js" />
</Scripts>
</asp:ScriptManager>
To add a reference to a Web Service:
<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:
- 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). - Non Visual Components : They don't have any user interface. They derive from the
Sys.Component
class.
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.
Type.registerNamespace("Sample");
Sample.TestComponent = function() {
Sample.TestComponent.initializeBase(this);
}
Sample.TestComponent.prototype = {
initialize : function() {
Sample.TestComponent.callBaseMethod(this, 'initialize');
alert("Initialize called on TestComponent");
},
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:
Sys.Application.add_init(pageInit);
function pageInit() {
$create(Sample.TestComponent, {'id':'testComponent1'});
}
function pageLoad() {
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:
$create(ComponentName, {Dictionary of properties },
{ Dictionary of events}, {Dictionary of refrences},
$get(associatedElementID));
For a component, we don't need associatedElementID
, but for Behavior
s and Control
s, 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:
$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:
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:
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:
Type.registerNamespace("Sample");
Sample.TestBehavior = function(element) {
Sample.TestBehavior.initializeBase(this,[element]);
}
Sample.TestBehavior.prototype = {
initialize : function() {
Sample.TestBehavior.callBaseMethod(this, 'initialize');
alert("Initialize called on TestComponent");
},
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:
$create(BehaviorName, {Dictionary of properties },
{ Dictionary of events}, {Dictionary of refrences} ,
$get(associatedElementID));
The $find
will look like:
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:
Type.registerNamespace("Sample");
Sample.TestControl = function(element) {
Sample.TestControl.initializeBase(this,[element]);
}
Sample.TestControl.prototype = {
initialize : function() {
Sample.TestControl.callBaseMethod(this, 'initialize');
alert("Initialize called on TestComponent");
},
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 Behavior
s. 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.
ScriptComponentDescriptor
s can be used to create non-visual components, and similarly, ScriptBehaviorDescriptor
and ScriptControlDescriptor
can be used to create Behavior
s and Control
s.
In my next article, I will explain how AJAX enabled server controls work.