The Microsoft Ajax Library JavaScript Type Extensions

Building on the simple JavaScript type system, the Microsoft AJAX Library adds a type system through simple JavaScript conventions. The type system is defined using the Type AJAX class and is described in the following sections.

Tip

Tip

To best understand the AJAX library, you should open the source code for MicrosoftAjax.debug.js in Visual Studio. The JavaScript files are available in a stand-alone download from http://ajax.asp.net.

The JavaScript Type

The Type class defined in the Microsoft Ajax Library is a special kind of library component that extends the JavaScript Function class. (All JavaScript functions derive from Function.) In the Microsoft AJAX Library, the JavaScript Type class is defined by referencing the global JavaScript Function class, as the following line from the library’s source code demonstrates:

window.Type = Function;

Because Type extends Function, all prototype methods of Type are available as instance methods of functions. This lets the Microsoft AJAX Library add a type system for namespaces, classes, and inheritance models similar to those found in managed languages such as C# and other .NET Framework languages.

Tip

Tip

Because a class does not exist in the JavaScript language, the term class (when speaking about JavaScript) refers to a function that is written as a logical class implementation and is designed to be called with the new operator, or a type that is defined using the Microsoft AJAX Library type system.

JavaScript Namespaces

With client-side script libraries, you should create namespaces as appropriate to organize classes, just as you would in compiled class libraries. The need for namespaces is especially true as you build larger client script libraries and interact with custom libraries from vendors or open source projects. To register a namespace, use the method Type.registerNamespace. This method creates a namespace object under which types in your namespace are created.

To create the namespace SOAjax.Controls, use the following script, which creates a root level namespace for SOAjax with a child namespace Controls:

Type.registerNamespace('SOAjax.Controls'),

To see an example of how a namespace is registered in low-level code, you can look at the Microsoft AJAX Library itself. The following snippet defines the Sys namespace in the Microsoft AJAX Library. You should avoid using this syntax, but it is helpful as an illustration of exactly what a namespace is.

// from MicrosoftAjax.debug.js, demonstrating low-level namespace implementation
window.Sys = {
    __namespace: true,
    __typeName: "Sys",
    getName: function() {return "Sys";}
};

When you define JavaScript libraries, you should create namespaces to distinguish your types from other class libraries and to help organize your code in conceptual buckets. For example, consider the Microsoft AJAX Library—core objects are defined in Sys, network libraries are defined in Sys.Net, and UI libraries are defined in Sys.UI.

Types and Inheritance

The Microsoft AJAX Library extends the object type through the registerClass and inheritsFrom functions for working with classes and inheritance. These functions are used by the framework to manage the type system and inheritance chains. They are also used to assign base class behaviors to inheriting classes. The main reason to use inheritance is to make code reusable by assigning common behaviors through base classes. In JavaScript, you can also share methods between types through prototype assignment. Instead of building a complex type system for business objects and database persistence as you might in C#, the main reason to use inheritance in the AJAX library is to develop components and controls that are managed through the Sys.Application object. The classes you build in JavaScript will be used to encapsulate user interface functionality rather than business logic, which is handled through back-end services.

In the following code samples, I’ll show you some simple extensions to DOM elements. You’ll learn more about components and controls in the next section of the book, "Applied AJAX," but for now we’ll use simple JavaScript classes to demonstrate inheritance. Because typically (but not always) you’ll be using JavaScript class libraries to build out user interface libraries and not business logic, I’ll focus on DOM-based programming.

To build a class definition, start out with a function. Because JavaScript is an object-based language, all functions are equivalent to class definitions and can be called with the new keyword to create a new object instance. A function can also be assigned to an object—in most cases you’ll be creating functions that exist within a namespace, which is itself really just an object. For example, the following code creates the namespace SOAjax.Examples and creates the class BaseControlExtension as a member of the namespace.

Type.registerNamespace("SOAjax.Examples");
SOAjax.Examples.BaseControlExtension = function(domElement, args) { }

To assign methods to a type, you assign methods to its prototype. There are two syntaxes that you will see when working with the Microsoft AJAX Library, and either one is valid. The first syntax creates methods as functions and assigns the functions to the prototype. This syntax is optimal for large class libraries, such as the Microsoft AJAX Library’s core framework, but is less readable. The following code sample uses this syntax to create a function SOAjax$Examples$BaseControlExtension$init and assign it to the init method using the prototype. Notice that the dollar sign is an arbitrary separator character used by convention throughout the AJAX library.

function SOAjax$Examples$BaseControlExtension$init(domElement, args) {
    Sys.Debug.trace('Example code.')
}
SOAjax.Examples.BaseControlExtension.prototype{
    init : SOAjax$Examples$BaseControlExtension$init
}

Another way to write this code is to assign the prototype method in the function declaration:

SOAjax.Examples.BaseControlExtension.prototype.init =
    function SOAjax$Examples$BaseControlExtension$init(domElement, args) {
        Sys.Debug.trace('Example code.')
    }

The standard syntax, which is also the preferred best practice for custom libraries because it is the most readable, defines methods directly in the prototype. The following code is syntactically equivalent to the earlier code samples but defines the method inline in the prototype.

SOAjax.Examples.BaseControlExtension.prototype{
    init : function(domElement, args) {
        Sys.Debug.trace('Example code.')
    }
}

To create a base class for a control extension (an object that will interact with a DOM element), you can create a base class (called BaseControlExtension here) that takes a domElement in its constructor and has an optional argument list. In this example, I also create an init method that creates a floating DIV element based on the location of the target element. I add a click handler that can hide the extension and dispose logic that deletes the class instance. Example 4-4 demonstrates the base class that can then be implemented for specific use.

Example 4-4. The BaseControlExtension class forms a base class for DOM-based functionality (Web/Script/ClientLibrary.js).

Type.registerNamespace("SOAjax.Examples");
SOAjax.Examples.BaseControlExtension = function(domElement, args) {
    this.init(domElement,args);
}

SOAjax.Examples.BaseControlExtension.prototype = {
    target: null,
    extendedControl: null,
    timeOut: null,

    init: function(domElement, args) {

        this.target = domElement;
        this.target.control = this;

        this.extendedControl = document.createElement('DIV'),
        Sys.UI.DomElement.setVisible(this.extendedControl, false);
        document.body.appendChild(this.extendedControl);

        // Sets the location of the DOM element, discussed in chapter 5.
        var loc = Sys.UI.DomElement.getLocation(this.target);
        var bounds = Sys.UI.DomElement.getBounds(this.target);
        Sys.UI.DomElement.setLocation(this.extendedControl,
           loc.x + 15, loc.y - bounds.height);
        Sys.UI.DomElement.setVisible(this.extendedControl, true);

        this.clickDelegate = Function.createDelegate(this, this.dispose);
        $addHandler(this.extendedControl, 'click', this.clickDelegate);
    },

    dispose: function() {
        this.target.control = null;
        document.body.removeChild(this.extendedControl);
        if (this.clickDelegate) delete this.clickDelegate;
    }
}
SOAjax.Examples.BaseControlExtension.registerClass(
    'SOAjax.Examples.BaseControlExtension'),

In Example 4-4, we created base functionality and registered the class with the AJAX library type system. To create a concrete class that implements the BaseControlExtension, we can create another class and register it with the type system as well. To register the Tooltip class with the type system and to inherit the base class functionality, use the registerClass method of the type itself, passing in the name of the class plus the type of the class you’re inheriting from. The following syntax is used to register a concrete class as inheriting from a base abstract class:

concreteClass.registerClass('concreteClass', abstractClass);

For example, to register the Tooltip class as inheriting from BaseControlExtension, call the registerClass method of the Tooltip type as follows, passing in the type of the base class as a parameter:

SOAjax.Examples.Tooltip.registerClass('SOAjax.Examples.Tooltip',
    SOAjax.Examples.BaseControlExtension);

To override a method of the base class, define the method in the concrete class prototype. Within the method, you can call the base class method with the type’s callBaseMethod function. The callBaseMethod function is a method of the defined type that also takes as parameters an instance parameter (usually this), the name of the method, and then optional parameters, which matches the base class’s signature. Similar to the type’s registerNamespace method, the callBaseMethod function is part of the type. For example, to call the init method of the Tooltip’s base class, the following syntax is used.

init: function(domElement, args) {
    SOAjax.Examples.Tooltip.callBaseMethod(this, 'init', [domElement, args]);
}

Example 4-5 completes the earlier code sample by creating a Tooltip class that inherits from the base BaseControlExtension class and provides simple tooltip functionality.

Example 4-5. To create an inheriting class (a concrete class), use the registerClass method and the callBaseMethod function (Web/Script/ClientLibrary.js).

SOAjax.Examples.Tooltip = function(domElement, args) {
    this.init(domElement, args);
}
SOAjax.Examples.Tooltip.prototype = {
    init: function(domElement, args) {
        SOAjax.Examples.Tooltip.callBaseMethod(this, 'init', [domElement, args]);
        this.extendedControl.style.border = '1px solid black';
        this.extendedControl.style.backgroundColor = 'yellow';
        this.extendedControl.style.padding = '3px';
        this.extendedControl.innerHTML = args.message;
    },

    dispose: function() {
        SOAjax.Examples.Tooltip.callBaseMethod(this, 'dispose'),
    }
}

SOAjax.Examples.Tooltip.registerClass('SOAjax.Examples.Tooltip',
    SOAjax.Examples.BaseControlExtension);

To use this control in a page, you can simply create a tooltip using the new keyword of the Tooltip class.

var tip = new SOAjax.Examples.Tooltip($get('TestDiv'), {message :'Hello, world'});

In JavaScript, it is best to avoid creating deep hierarchical class models, as you would in compiled languages such as C#, and focus on behavioral inheritance instead. A shallow class inheritance chain performs better, is more effective, and is easier to debug. Because of the loosely typed nature of JavaScript, in which objects are flexible, I favor a behavioral inheritance model and prefer to assign behaviors as needed. As a general rule, a goal in designing JavaScript type libraries is to create as few type definitions as possible and to create general purpose, reusable components that can extend HTML elements and browser functionality.

Now that you have an understanding of the type extensions of ASP.NET AJAX, we’ll look more closely at the AJAX library’s client framework in the next chapter. The client framework is a lightweight JavaScript runtime with support for most browser-oriented programming tasks, including DOM manipulation, network calls, components, and controls. You’ll see that control implementation is missing from the framework and is left instead to third-party developers and the AJAX Control Toolkit, a collaboration between Microsoft and the ASP.NET community. The ASP.NET AJAX Control Toolkit can be downloaded from www.codeplex.com/ajaxcontroltoolkit.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset