From a developer-oriented perspective, AJAX
collectively refers to a set of development components, tools, and
techniques for creating highly interactive Web applications that give
users an overall better experience. According to the AJAX paradigm, Web
applications work by exchanging data rather than pages with the Web
server. From a user perspective, this means that faster roundtrips
occur and, more importantly, page loading is quicker and the need for
refreshing the page entirely is significantly reduced.
As
a result, a Web application tends to look like a classic desktop
Microsoft Windows application and can invoke server code from the
client, run and control server-side asynchronous tasks, and feature a
strongly responsive and nonflickering user interface. An AJAX
application can have a number of features that minimize user
frustration, provide timely feedback about what’s going on, and deliver
great mashed-up content. (Hold on! This doesn’t mean AJAX Web
applications are the same as desktop applications; they simply allow
for a few more desktop-like features.)
Note
AJAX
applications have a number of plusses but also a few drawbacks.
Overall, choosing an AJAX application rather than a classic Web
application is simply a matter of weighing the trade-offs. An AJAX
application certainly gives users continuous feedback and never appears
held up by some remote operation. On the other hand, AJAX applications
are not entirely like desktop applications, and their capabilities in
terms of graphics, multimedia, and hardware control are not as powerful
as in a regular (smart) client. In the end, AJAX applications are just
one very special breed of a Web application; as such, they might
require some code refactoring to deliver the expected performance and
results. |
We
are all witnessing and contributing to an interesting and unique
phenomenon—the Web is undergoing an epochal change right before our
eyes as a result of our actions. Ten years ago, the Web was in its
infancy and based on an infrastructure that was simple, ubiquitous, and
effective. Ten years of Web evolution has resulted in the building of a
thick layer of abstraction on the server side, but it hasn’t changed
the basic infrastructure—HTTP protocol and pages. The original
infrastructure, which was the chief factor for the rapid success of the
Web model of applications, is still there.
In
fact, the next generation of Web applications will still be based on
the HTTP protocol and pages. However, the contents of pages and the
capabilities of the server-side and client-side machinery will change
to provide a significantly richer user experience.
The Hidden Engine of AJAX
Today
Web applications work by submitting user-filled forms to the Web server
and displaying the markup returned by the Web server. The
client-to-server communication employs the HTTP protocol and is usually
conducted by the browser. The new model that AJAX heralds simply
employs an alternate and scriptable tool to conduct the HTTP
communication with the Web server. The benefit is that the page that
triggers the call remains up and running and refreshes its document
object model (DOM) with the freshly downloaded data. No page
replacement occurs, and the overall user experience is smooth and
continual.
The Classic Browser-Led Model
Using
the local Domain Name System (DNS) resolver in the operating system,
the browser resolves the requested URL to an IP address and opens a
socket. An HTTP packet travels over the wire to the given destination.
The packet includes the form and all its fields. The request is
captured by the Web server and typically forwarded to an internal
module for further processing. At the end of the process, an HTTP
response packet is prepared and the return value for the browser is
inserted in the body. If the response contains an HTML page, the
browser replaces the current contents entirely with the new chunk of
markup.
While
the request is being processed on the server, the “old” page is frozen
but still displayed to the client user. As soon as the “new” page is
downloaded, the browser clears the display and renders the page.
This
model was just fine in the beginning of the Web age when pages
contained little more than formatted text, hyperlinks, and some images.
The success of the Web has prompted users to ask for increasingly more
powerful features, and it has led developers and designers to create
more sophisticated services and graphics. The net effect is that pages
are heavy and cumbersome—even though we still insist on calling them
“rich” pages. Regardless of whether they’re rich or just cumbersome,
these are the Web pages of today’s applications. And nobody really
believes that we’re going to return to the scanty and spartan HTML
pages of a decade ago.
Given
the current architecture of Web applications, each user action requires
a complete redraw of the page. Subsequently, richer and heavier pages
render slowly and, as a result, produce a good deal of flickering.
Projected to the whole set of pages in a large, portal-like
application, this mechanism is just perfect for unleashing the
frustrations of the poor end user.
The New Out-of-Band Model
The chief factor that enables AJAX functionality in a Web page is the ability to issue out-of-band
HTTP requests. In this context, an out-of-band call indicates an HTTP
request placed using a component different from the browser. The
out-of-band call is triggered via script by an HTML page event and is
served by a proxy component. In AJAX frameworks, the proxy component is
based on the XMLHttpRequest object.
Note
About
a decade ago, there was a team at Microsoft working on a technology
called Remote Scripting (RS). RS never reached the stage of a version
1.0 but had a lot in common with today’s AJAX hidden engine. In RS, the
proxy component was a Java applet managing the browser-to-server
communication. |
XMLHttpRequest
is a browser’s object that is scriptable through JavaScript. It sends a
regular HTTP request to the specified URL and waits, either
synchronously or asynchronously, for it to be fully served. When the
response data is ready, the proxy invokes a user-defined JavaScript
callback to refresh any portion of the page that needs updating. Figure 1 provides a graphical overview of the model.
All
browsers know how to replace an old page with a new page; until a few
years ago, though, not all of them provided an object model to
represent the current contents of the page. (Today, I can hardly
mention a single modern, commercially available browser that doesn’t
expose a read/write page DOM.) For browsers that supply an updatable
object model for HTML pages, the JavaScript callback function can
refresh specific portions of the old page, thus making them look
updated, without a full reload.
Exactly
what are the capabilities required of a browser to run AJAX
functionalities? As mentioned, a browser needs to provide two key
capabilities: a proxy mechanism to make client code able to place
out-of-band HTTP calls, and an updatable DOM. And both capabilities
must be achieved through standard and globally accepted interfaces.
There’s
a World Wide Web Consortium (W3C) ratified standard for the updatable
DOM. A W3C standard for the proxy component is currently being
developed. It takes the form of the existing XMLHttpRequest
object and is devised as an interface exposed by the browser to allow
script code to perform HTTP client functionality, such as submitting
form data or loading data from a remote Web site. The latest working
draft is available at http://www.w3.org/TR/XMLHttpRequest.
In addition, browsers must support JavaScript and, preferably, cascading style sheets (CSS).
The Role of the HTML Document Object Model
The
page Document Object Model (DOM) is the specification that defines a
platform- and language-neutral interface for accessing and updating the
contents, structure, and style of HTML and XML documents. As a
recognized standard ratified by the W3C committee, the DOM is now
supported by virtually all browsers. The DOM provides a standard set of
objects for representing the constituent elements of HTML and XML
documents. All together, these objects form a standard interface for
accessing and manipulating child elements of HTML pages and, more in
general, XML documents.
The
DOM application programming interface (API) renders the displayed page
as a tree-based structure. For a Web page, each node maps to an object
that represents an HTML tag. The
object, therefore, has properties and methods that can be applied to an
HTML tag. There are three fundamental operations you can accomplish on
a node: find the node (including related nodes such as children,
parent, or sibling nodes), create a node, and manipulate a node.
Identifying a particular node is easy as long as the page author knows
the ID of the corresponding element.
The
W3C DOM consists of three levels that indicate, for the browser, three
different levels of adherence to the standard. For more information,
take a look at http://www.w3.org/DOM.
From Dynamic HTML to the Standard DOM
About
ten years ago, with Internet Explorer 4.0, Microsoft introduced a
proprietary object model named Dynamic HTML (DHTML) to enable page
authors to update the current page dynamically using JavaScript. The
success of DHTML led to the definition of a standard document object
model—the W3C’s DOM. Quite obviously, the DOM evolved from DHTML and
became much more generalized than DHTML.
Today
most browsers support a mix of DOM and DHTML. Which one should you use?
In particular, to update some contents, should you obtain a reference
to the textual child node of the node that matches the intended HTML
tag (the DOM way) or just grab a reference to a node and use the innerHTML
property as you would do in the DHTML way? Likewise, to add a new
element, should you create a new element or just stuff in a chunk of
updated HTML via innerHTML?
Admittedly, one of the most interesting debates in the community is
whether to use DHTML to manipulate pages or opt for the cleaner
approach propounded by the DOM API.
The key fact is that the DOM API is significantly slower than using innerHTML.
If you go through the DOM to generate some user interface dynamically,
you have to create every element, append each into the proper
container, and then set properties. The alternative only entails that
you define the HTML you want and render it into the page using innerHTML. The browser, then, does the rest by rendering your markup into direct graphics.
Overall,
DHTML and DOM manipulation are both useful depending on the context.
There are many Web sites that discuss performance tests, and DHTML is
always the winner. Anyway, DOM is still perfectly fast as long as you
use it the right way—that is, create HTML fragments and append them to
the proper container only as the final step.
Note
The
inclusion of a standard API to edit the currently displayed page is a
key factor in the success of AJAX. Although the first working
frameworks for AJAX-like remote scripting date back to a decade ago,
the limited support that browsers have had for dynamic document changes
through most of this time period slowed down the adoption of such
technologies in the industry. Until now. With the HTML DOM becoming a
widely supported standard and the availability of a mature tool for
remote calls such as XMLHttpRequest, AJAX is ready for prime time. |
The XMLHttpRequest Object
Created by Microsoft and adopted soon thereafter by Mozilla, the XMLHttpRequest
object is fully supported these days by the majority of Web browsers.
The implementation can significantly differ from one browser to the
next, even though the top-level interface is nearly identical. For this
reason, a W3C committee is at work with the goal of precisely
documenting a minimum set of interoperable features based on existing
implementations. An excellent presentation on the component can be
found here: http://developer.mozilla.org/en/docs/XMLHttpRequest.
Note
The XMLHttpRequest
object originally shipped as a separate component with Internet
Explorer 5.0 back in the spring of 1999. It is a native component of
all Microsoft operating systems that have shipped since. In particular,
you’ll certainly find it installed on all machines that run Windows
2000, Windows XP, and newer operating systems. |
When the XMLHttpRequest
object was first released, the Component Object Model (COM) was ruling
the world at Microsoft. The extensibility model of products and
applications was based on COM and implemented through COM components.
In the late 1990s, the right and natural choice was to implement this
new component as a reusable automation COM object, named Microsoft.XmlHttp.
COM objects are external components that require explicit permission to run inside of a Web browser. In particular, to run the XMLHttpRequest
object, and subsequently enable any AJAX functionality built on top of
it, at a minimum a client machine needs to accept ActiveX components
marked safe for scripting. (See Figure 2.)
The XMLHttpRequest
object is certainly a safe component, but to enable it users need to
decrease their security settings and accept any other component
“declared” safe for scripting that is around the Web sites they visit.
Mozilla adopted XMLHttpRequest immediately after its first release with Internet Explorer 5.0. However, in Mozilla-equipped browsers, the XMLHttpRequest
object is part of the browser’s object model and doesn’t rely on
external components. Put another way, a Mozilla browser such as Firefox
publishes its own XMLHttpRequest
object into the scripting engine and never uses the COM component, even
when the COM component is installed on the client machine and is part
of the operating system.
As a result, in Mozilla browsers, XMLHttpRequest looks like a native JavaScript object and can be instantiated through the classic new operator:
// The object name requires XML in capital letters
var proxy = new XMLHttpRequest();
When the browser is Internet Explorer (up to version 6.0), the XMLHttpRequest object is instantiated using the ActiveXObject wrapper, as shown here:
var proxy = new ActiveXObject("Microsoft.XmlHttp");
Generally, AJAX-style frameworks check the current browser and then decide which route to take.
Implemented as a COM component for historical reasons on Internet Explorer browsers, the XMLHttpRequest
object has finally become a browser object with Internet Explorer 7.0.
All potential security concerns are removed at the root. Needless to
say, implemented as a browser object, the XMLHttpRequest
functionality is somewhat safer, at least in the sense it doesn’t
require users to change their security settings for the browser.
Using the XMLHttpRequest Object
The XMLHttpRequest
object is designed to perform one key operation: sending an HTTP
request. The request can be sent either synchronously or
asynchronously. The following bit of code shows the programming
interface of the object as it results from the W3C working draft at the
time of this writing:
interface XMLHttpRequest
{
function onreadystatechange;
readonly unsigned short readyState;
void open(string method, string url);
void open(string method, string url, bool async);
void open(string method, string url, bool async, string user);
void open(string method, string url, bool async,
string user, string pswd);
void setRequestHeader(string header, string value);
void send(string data);
void send(Document data);
void abort();
string getAllResponseHeaders();
string getResponseHeader(string header);
string responseText;
Document responseXML;
unsigned short status;
string statusText;
};
Using
the component is a two-step operation. First, you open a channel to the
URL and specify the method (GET, POST, or other) to use and whether you
want the request to execute asynchronously. Next, you set any required
header and send the request. If the request is a POST, you pass to the send method the body of the request.
The send method returns immediately in the case of an asynchronous operation. You write an onreadystatechange
function to check the status of the current operation and, using that
function, figure out when it is done. The following code shows how to
carry on a POST request using the XMLHttpRequest object:
var xmlRequest, e;
try
{
xmlRequest = new XMLHttpRequest();
}
catch(e)
{
try
{
xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
}
}
// Prepare for a synchronous POST request
var body = null; // An empty request body this time...
xmlRequest.open("POST", pageUrl, false);
xmlRequest.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
xmlRequest.send(body);
In a synchronous call, the send
method returns when the response has been fully downloaded and parsed
by the object. You can access it as a plain string using the responseText property. If the response is an XML stream, you can have it exposed as an XML DOM object using the responseXml property.
Important
If
you’re going to use ASP.NET AJAX or any other AJAX-like framework for
building your applications, you’ll hardly hear anything about the XMLHttpRequest
object, much less use it directly in your own code. An AJAX framework
completely encapsulates this object and shields page authors and
application designers from it. You don’t need to know about XMLHttpRequest to write great AJAX applications, no matter how complex and sophisticated they are. However, knowing the fundamentals of XMLHttpRequest can lead you to a better and more thorough understanding of the platform and to more effective diagnoses of problems. |
The Microsoft AJAX JavaScript Library
Most
of the power of AJAX resides on the client and is strictly related to
the browser’s and platform’s client-side functionality. No AJAX
capability would ever be possible without a client-side engine; and
this engine can only be written in JavaScript. Such a script code
governs the execution of out-of-band calls and often kicks in and
replaces regular postbacks with AJAX postbacks. Moreover, no AJAX
functionality would ever be possible without JavaScript and a standard
(and rich) DOM. The DOM, though, is not enough.
The
DOM represents the programming gateway to the page constituent
elements, but it is not designed to provide programming facilities such
as those you can find in a general-purpose library. Normally, the
script tools you can leverage to consume objects and contents from the
DOM are those provided by the JavaScript language. Not exactly a
powerful toolkit. Enter the Microsoft AJAX JavaScript library.
The
AJAX extensions to ASP.NET silently leverages the Microsoft AJAX
JavaScript library for all of its built-in features. The library,
though, is also available to page authors to code their own JavaScript
page-specific functions.
The
Microsoft AJAX library is written in JavaScript, although with a strong
sense of object-orientation. The JavaScript language does support
objects and allow the creation of custom objects. It does not, however,
support full object-orientedness because it has no native concept of
true object inheritance. Nonetheless, even excluding true object
orientation, JavaScript is still a modern and suitable language that
can be used to build a class framework à la
the .NET Framework. ASP.NET AJAX takes the JavaScript language to the
next level by adding some type-system extensions and the notions of
namespace and inheritance. In addition, the ASP.NET AJAX JavaScript
supports interfaces and enumerations, and it has a number of helper
functions to manipulate strings and arrays.
These
extensions are coded using the base set of instructions that
characterize the core JavaScript language, and they’re persisted to a
set of .js files. These .js
files are not installed as distinct files on the Web server when you
install ASP.NET. They are embedded as resources into the ASP.NET AJAX
assembly—system.web.extensions. If you want them available as distinct files (for example, for your home perusal), go to http://msdn2.microsoft.com/en-us/asp.net/bb944808.aspx, check the license agreement, and get them as a single downloaded compressed file.
Let’s
now dig out some of these extensions to the JavaScript language and
briefly explore the features of the built-in classes.
Important
The Microsoft AJAX JavaScript library is self-contained in the .js
files you get from the aforementioned URL. This means that you can
embed these files in any Web page and enjoy the object-oriented
features of JavaScript regardless of whether or not ASP.NET AJAX is
being used to power the page. For example, you can use the <script>
tag to include all required JavaScript files in a PHP or classic ASP
page and enjoy the advanced capabilities of the Microsoft AJAX
JavaScript library. |
JavaScript Language Extensions
The JavaScript language features a set of built-in objects, including Function, Object, Boolean, Array, Number, and String. All intrinsic objects have a read-only property named prototype. You use the prototype
property to provide a base set of functionality shared by any new
instance of an object of that class. New functionality can be added to
the class prototype inside of an application to extend and improve the
capabilities of a given class. This is exactly what the Microsoft AJAX
library does.
Type
information is the most important aspect of the JavaScript language
that has been enhanced. Aside from instances of the base types,
everything else in JavaScript is a plain object.
With the library extensions, you have a type information system that is
similar to the .NET Framework. The following code now works and
displays “Person” instead of a generic “object” string:
var p = new Person("Dino", "Esposito");
alert(Object.getTypeName(p));
But where does the getTypeName method come from? It is a new method defined on the native JavaScript Object type. The Microsoft AJAX library contains code that defines new objects and extends existing JavaScript objects with additional functionality. Table 1 lists the main global objects defined in the library.
Table 1. Top-Level Objects in the Microsoft AJAX Library
Property | Description |
---|
Array | Extends the native Array
object. This object groups static methods to add, insert, remove, and
clear elements of an array. It also includes static methods to
enumerate elements and check whether a given element is contained in
the array. |
Boolean | Extends the native Boolean object. This object defines a static parse method to infer a Boolean value from a string or any expression that evaluates to a Boolean value. |
Date | Extends the native Date object with a couple of instance methods: localeFormat and format. These methods format the date using the locale or invariant culture information. |
Error | Defines a static create method to wrap the JavaScript Error object and add a richer constructor to it. This object incorporates a couple of properties—message and name—to
provide a description of the error that occurred and identify the error
by name. A number of built-in error objects are used to simulate
exceptions. In this case, the name property indicates the name of the exception caught. |
Function | Extends the native Function object. This object groups methods to define classes, namespaces, delegates, and a bunch of other object-oriented facilities. |
Number | Extends the native Number object. This object defines a static parselocaleFormat and format.
method to infer a numeric value from a string or any expression that
evaluates to a numeric value. In addition, it supports a pair of static
formatting methods: |
Object | Extends the native Object object. This object groups methods to read type information, such as the type of the object being used. |
RegExp | Wraps the native RegExp object. |
String | Extends the native String object. This object groups string manipulation methods, such as trim methods and endsWith and startsWith methods. In addition, it defines static localeFormat and format methods that are close relatives of the String.Format method of the managed String type. |
After the Microsoft AJAX library has been added to the application, the following code will work just fine:
var s = "Dino";
alert(s.startsWith('D'));
The native JavaScript String object doesn’t feature either a startsWith or an endsWith method; the extended AJAX String object does.
One
of the most common mistakes made when writing script code inside of Web
pages is to use direct access to HTML elements instead of resorting to
the getElementById method of the DOM. Suppose you have a text box element named TextBox1 in your client page. The following script code won’t work on all browsers:
The correct form ratified by the W3C paper for the HTML DOM standards is shown here:
alert(document.getElementById("TextBox1").value);
The
correct form is clearly more verbose and bothersome to write over and
over again. The Microsoft AJAX library comes to the rescue with the $get global function. Simply put, the $get function is a shortcut for the document.getElementById function. If the Microsoft AJAX library is in use, the following expression is fully equivalent to the one just shown:
alert($get("TextBox1").value);
The $get function has two overloads. If you call $get passing the sole ID, the function falls back into document.getElementById. Alternatively, you can specify a container as the second argument, as shown here:
var parent = $get("Div1");
$get("TextBox1", parent);
If the container element supports the getElementById method, the function returns the output of element.getElementById; otherwise, the $get
function uses the DOM interface to explore the contents of the subtree
rooted in the element to locate any node with the given ID.
Object-Oriented Extensions: Namespaces
In JavaScript, the Function object is the main tool you use to combine code with properties and forge new components. In the Microsoft AJAX library, the Function object is extended to incorporate type information, as well as namespaces, inheritance, interfaces, and enumerations.
A
namespace provides a way of grouping and classifying types belonging to
a library. A namespace is not a type itself, but it adds more
information to the definition of each type it contains to better
qualify the type. By default, all custom JavaScript functions belong to
the global space of names. In the Microsoft AJAX library, you can
associate a custom function with
a particular namespace, for purely organizational reasons. When
declaring a custom type in the Microsoft AJAX library, you can do as
follows:
Type.registerNamespace("Core35");
Core35.Person = function Core35$Person(firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}
// Define the body of members
function Core35$Person$ToString() {
return this._lastName + ", " + this._firstName;
}
...
// Define the prototype of the class
Core35.Person.prototype = {
ToString: Core35$Person$ToString,
get_FirstName: Core35$Person$get_FirstName,
set_FirstName: Core35$Person$set_FirstName,
get_LastName: Core35$Person$get_LastName,
set_LastName: Core35$Person$set_LastName
}
// Register the class, extending our own IntroAjax Person class
IntroAjax.Person.registerClass("Core35.Person");
The Type.registerNamespace method adds the specified namespace to the runtime environment. In a way, the registerNamespace method is equivalent to using the namespace {...} construct in C# or the Namespace .. End Namespace construct in Microsoft Visual Basic.
The Core35.Person function defined following the namespace declaration describes a Person type in the Core35
namespace. Finally, the newly defined function must be registered as a
class with the Microsoft AJAX library framework. You use the registerClass method on the current function. The registerClass method is defined in the prototype of the Function object; as such, it is inherited by all functions. Internally, the registerClass method sets the _typeName property of the function to the first parameter of the method—the actual name of the class.
The registerClass
method takes a number of parameters. The first parameter is mandatory,
and it indicates the public name that will be used to expose the
JavaScript function as a class. Additional and optional parameters are
the parent class, if there is any, and any interface implemented by the
class.
Note
In
the definition of a new class, you can use an anonymous function or a
named function. In terms of syntax, both solutions are acceptable. The
convention, though, is that you opt for named functions and name each
function after its fully qualified name, replacing the dot symbol (.)
with a dollar symbol ($). |
Object-Oriented Extensions: Inheritance
Let’s define a new class, Citizen, that extends Person by adding a couple of properties: an address and a national identification number. Here’s the skeleton of the code you need:
// Declare the class
Core35.Citizen = function Core35$Citizen(firstName, lastName, id)
{
...
}
// Define the prototype of the class
Core35.Citizen.prototype = {
...
}
// Register the class
Core35.Citizen.registerClass("Core35.Citizen", Core35.Person);
Note that the first argument of registerClass is a string, but the second one has to be an object reference. Let’s flesh out this code a bit.
In
the constructor, you’ll set some private members and call the base
constructor to initialize the members defined on the base class. The initializeBase method (defined on Function) retrieves and invokes the base constructor:
Core35.Citizen = function Core35$Citizen(firstName, lastName, id)
{
Core35.Citizen.initializeBase(this, [firstName, lastName]);
this._id = id;
this._address = "";
}
You pass initializeBase
the reference to the current object as well as an array with any
parameters that the constructor to call requires. You can use the [...] notation to define an array inline. If you omit the [...] notation, be ready to handle a parameter count exception.
Quite
often, developers derive a class because they need to add new members
or alter the behavior of an existing method or property.
Object-oriented languages define a proper keyword to flag members as
overridable. How is that possible in JavaScript? By simply adding a
member to the class prototype, you mark it as overridable in derived
classes. In addition, if the member already exists on the base class,
it is silently overridden in the new one. Here’s the prototype of the Citizen class:
Core35.Citizen.prototype =
{
ToString: Core35$Citizen$ToString,
get_ID: Core35$Citizen$get_ID,
get_Address: Core35$Citizen$get_Address,
set_Address: Core35$Citizen$set_Address
}
The class has a read-only ID property and a read-write Address property. Furthermore, it overrides the ToString method defined in the parent class:
function Core35$Citizen$ToString()
{
var temp = Core35.Citizen.callBaseMethod(this, 'ToString');
temp += " [" + this._id + "]";
return temp;
}
You use callBaseMethod to invoke the same method on the parent class. Defined on the Function class, the callBaseMethod
method takes up to three parameters: the instance, the name of the
method, plus an optional array of arguments for the base method.
As mentioned earlier, the ToString method on the Person class returns a LastName, FirstName string. The ToString method on the Citizen class returns a string in the following format: LastName, FirstName [ID].
Object-Oriented Extensions: Interfaces
Finally,
an interface describes a group of related behaviors that are typical of
a variety of classes. In general, an interface can include methods,
properties, and events; in JavaScript, it contains only methods.
Keeping
in mind the constraints of the JavaScript language, to define an
interface you create a regular class with a constructor and a
prototype. The constructor and each prototyped method, though, will
just throw a not-implemented exception. Here’s the code for the sample Sys.IDisposable built-in interface:
Type.registerNamespace("Sys");
Sys.IDisposable = function Sys$IDisposable()
{
throw Error.notImplemented();
}
function Sys$IDisposable$dispose()
{
throw Error.notImplemented();
}
Sys.IDisposable.prototype =
{
dispose: Sys$IDisposable$dispose
}
Sys.IDisposable.registerInterface('Sys.IDisposable');
The following statement registers the Citizen class, makes it derive from Person, and implements the IDisposable interface:
Core35.Citizen.registerClass('Core35.Citizen',
Core35.Person, Sys.IDisposable);
To
implement a given interface, a JavaScript class simply provides all
methods in the interface and lists the interface while registering the
class:
function Core35$Citizen$dispose
{
this._id = "";
this._address = "";
}
Core35.Citizen.prototype =
{
dispose: Core35$Citizen$dispose
...
}
Note,
though, that you won’t receive any runtime error if the class that
declares to implement a given interface doesn’t really support all the
methods.
If a class implements multiple interfaces, you simply list all required interfaces in the registerClass method as additional parameters. Here’s an example:
Sys.Component.registerClass('Sys.Component', null,
Sys.IDisposable,
Sys.INotifyPropertyChange,
Sys.INotifyDisposing);
As you can see, in this case you don’t have to group interfaces in an array.
The Application Core Component
The
AJAX client library is made up of three main logical layers: JavaScript
extensions, core framework classes, and user-interface (UI) framework
classes. (See Figure 3.)
As
mentioned, JavaScript extensions add new methods and capabilities to
native JavaScript objects and enable registration methods to simulate
object-oriented constructs such as classes, namespaces, inheritance,
and interfaces. The UI framework includes base components to define
client behaviors, controls, DOM elements, and input devices such as
keyboard and mouse buttons. The core framework classes form a sort of
base library that incorporates a set of commonly used classes for event
handling, string manipulation, Web services, debugging, and network
operations.
The
execution of each ASP.NET AJAX page is controlled by an application
object that is instantiated in the body of the library. The application
object is an instance of a private class—the Sys._Application class. Whenever an ASP.NET AJAX page is loaded in the browser, an instance of the Sys._Application class is promptly created and assigned to the Sys.Application object:
Sys.Application = new Sys._Application();
In addition, each ASP.NET AJAX page is injected with the following script code:
<script type="text/javascript">
<!--
Sys.Application.initialize();
// -->
</script>
This
code is placed immediately after the closing tag of the page’s form,
and it commands the loading of any script files registered for loading
with the page’s script manager. As a result, the Sys.Application object is the nerve center of the ASP.NET AJAX page.
Note
JavaScript
has no notion of private members; therefore, private members are
conventionally indicated by the underscore symbol (_) prefixing their
names. |
The Sys.Application object serves one main purpose: providing access to page components. Its findComponent
method scrolls the runtime hierarchy of Microsoft AJAX components for
the current page until it finds a component with a matching ID. The
method has two possible prototypes:
Sys._Application.findComponent(id);
Sys._Application.findComponent(id, parent);
The
former overload takes the ID of the component, uses it to look up the
component, and then navigates the hierarchy all the way down from the
root. When a non-null parent argument is specified, the search is restricted to the subtree rooted in the context object. The id parameter must be a string; the parent
parameter must be a Microsoft AJAX library object. The method returns
the object that matches the ID, or it returns null if no such object is
found.
The Microsoft AJAX library also supports a shortcut for retrieving runtime components—the $find method. The $find method is an alias for findComponent:
var $find = Sys.Application.findComponent;
You
can use this method to locate all components created by server controls
and extenders, as well as by your own JavaScript code. You can’t use $find to locate DOM elements; for DOM elements, you must resort to $get.
When a page using the Microsoft AJAX library page first loads up, the load
event is fired for the client code to perform any required
initialization. Note that the event refers to the page lifetime, not
the application lifetime. So whenever a classic postback occurs, you
receive a new load event. You don’t receive such events for any AJAX-style postback conducted via XMLHttpRequest. Likewise, the unload event is fired when the page is unloaded.
The load event occurs after a page has been loaded and initialized completely. For such a page, the load event is preferable to the browser’s onload for initialization purposes. Only when you get the Microsoft AJAX library load event, therefore, can you be sure that the page is ready for user interaction. The unload
event occurs just before the Microsoft AJAX library runtime releases
the page and all of its resources. For the sake of the application’s
stability, you should use this event instead of the browser’s onunload event for clean-up tasks.
The easiest way to define load and unload handlers is by means of predefined function names: pageLoad and pageUnload. These functions need to be global and parameterless:
<script type="text/JavaScript" language="JavaScript">
function pageLoad()
{
alert("Being loaded");
}
function pageUnload()
{
alert("Being unloaded");
}
</script>
Because this piece doesn’t directly call into any of the Microsoft AJAX library objects—including Sys.Application—you can safely place it everywhere, even at the top of the page.
The Network Stack
AJAX
libraries in general, and ASP.NET AJAX Extensions in particular, owe
their growing popularity to their ability to execute out-of-band Web
requests from the client. In particular, ASP.NET AJAX extensions allow
you to invoke Web service methods as well as static methods defined on
the code-behind page class. This ability leverages the networking
support built into the Microsoft AJAX library.
In the Microsoft AJAX library, a remote request is represented by an instance of the Sys.Net.WebRequest class. Table 2 lists the properties of the class.
Table 2. Properties of the Sys.Net.WebRequest class
Property | Description |
---|
body | Gets and sets the body of the request |
executor | Gets and sets the Microsoft AJAX library object that will take care of executing the request |
headers | Gets the headers of the request |
httpVerb | Gets and sets the HTTP verb for the request |
timeout | Gets and sets the timeout, if any, for the request |
url | Gets and sets the URL of the request |
The WebRequest class defines the url property to get and set the target URL and the headers
property to add header strings to the request. If the request is going
to be a POST, you set the body of the request through the body property. A request executes through the method invoke. The completed event informs you about the completion of the request.
Each
Web request is executed through an internal class—the Web request
manager—that employs an “executor” to open the socket and send the
packet. All executors derive from a common base class—the Sys.Net.WebRequestExecutor class.
The Microsoft AJAX library defines just one HTTP executor—the Sys.Net.XMLHttpExecutor class. As the name suggests, this executor uses the popular XMLHttpRequest object to execute the HTTP request.
Note
All AJAX libraries are associated with the XMLHttpRequest browser object. So what else could an executor be other than a reference to the XMLHttpRequest
browser object? In general, an HTTP executor is any means you can use
to carry out a Web request. An alternative executor might be based on
HTTP frames. The idea is to use a dynamically created inline frame to
download the response of a given request and then parse that result
into usable objects. |
DOM Events
Building
cross-browser compatibility for events is not an easy task. Internet
Explorer has its own eventing model, and so do Firefox and Safari. For
this reason, the event model of the Microsoft AJAX library is a new
abstract API that joins together the standard W3C API and the Internet
Explorer nonextensible model. The new API is closely modeled after the
standard W3C API.
In addition to using different method names (add/removeEventListener is for Firefox, and attach/detachEvent is for Internet Explorer), browsers differ in the way they pass event data down to handlers. In Internet Explorer, an event handler receives its data through the global window.event
object; in Firefox, the event data is passed as an argument to the
handler. In the Microsoft AJAX library, event handlers receive a
parameter with proper event data.
Another
significant difference is in the way mouse and keyboard events are
represented. The Microsoft AJAX library abstracts away any differences
between browsers by providing ad hoc enumerated types, such as Sys.UI.Key and Sys.UI.MouseButton. Here’s some sample code:
function button1_Click(e)
{
if (e.button === Sys.UI.MouseButton.leftButton)
{
...
}
}
function keyboard_EnterPressed(e)
{
if (e.keyCode === Sys.UI.Key.enter)
{
...
}
}
The Microsoft AJAX library provides a shorthand notation to create DOM event hookups and removal. For example, you can use the $addHandler and $removeHandler aliases to add and remove a handler. Here’s the syntax:
$addHandler(element, "eventName", handler);
$removeHandler(element, "eventName", handler);
In
many cases, you’ll want to hook up several handlers to a DOM event for
a component. Rather than manually creating all the required delegates
and related handlers, you can use a condensed syntax to add and remove
multiple handlers:
initialize: function()
{
var elem = this.get_element();
$addHandlers(
elem,
{[
'mouseover': this._mouseHoverHandler,
'mouseout': this._mouseOutHandler,
'focus', this._focusHandler,
'blur', this_blurHandler
]},
this);
}
The $clearHandlers alias, conversely, removes all handlers set for a particular DOM element in a single shot.
If
you write a component and wire up some events, it is essential that you
clear all handlers when the component is unloaded, or even earlier, if
you don’t need the handler any longer. For example, you should do that
from the component’s dispose
method to break circular references between your JavaScript objects and
the DOM. Correctly applied, this trick easily prevents nasty memory
leaks.
Note
You won’t receive any event data if you bind the handler via markup—for example, by setting the onclick attribute of an <input> tag. Everything said here applies only to event handlers added via methods (and aliases) of the Sys.UI.DomEvent
class. Events bound through attributes are still processed, but you
have to resort to your knowledge of the browser’s event model to
correctly grab associated information. |
Other Facilities
The
Microsoft AJAX library contains a number of other miscellaneous
components to provide additional facilities to ASP.NET AJAX developers.
The Sys.StringBuilder
class adds advanced text manipulation capabilities to ASP.NET AJAX
pages. As the name suggests, the class mimics the behavior of the
managed StringBuilder
class defined in the .NET Framework. When you create an instance of the
builder object, you specify initial text. The builder caches the text
in an internal array by using an element for each added text or line.
The Sys.StringBuilder object doesn’t accept objects other than non-null strings. The toString method composes the text by using the join method of the JavaScript array class.
The Microsoft AJAX library String class is also enriched with a format method that mimics the behavior of the Format method on the .NET Framework String class:
alert(String.format("Today is: {0}", new Date()));
You define placeholders in the format string using {n} elements. The real value for placeholders is determined by looking at the n.th argument in the format method call.
Another class that is worth mentioning is the Sys._Debug class. An instance of this internal class is assigned to the Sys.Debug global object:
Sys.Debug = new Sys._Debug();
In your pages, you use the Sys.Debug object to assert conditions, break into the debugger, or trace text. For example, the traceDump
method writes the contents of the specified object in a human-readable
format in the Microsoft AJAX library trace area. The trace area is
expected to be a <textarea> element with an ID of traceConsole. You can place this element anywhere in the page:
<textarea id="traceConsole" cols="40" rows="10" />
The traceDump method accepts two parameters, as shown here:
Sys.Debug.traceDump(object, name)
The name parameter indicates descriptive text to display as the heading of the object dump. The text can contain HTML markup. Figure 4 shows the results.