Chapter 7. Building an AJAX Class Library with Components

After completing this chapter, you will

  • Understand component-based development with the Microsoft AJAX Library.

  • Understand property and event implementations with the Microsoft AJAX Library.

  • Understand object-oriented AJAX programming using components.

  • Learn how to use AJAX components in your AJAX applications.

In the three preceding chapters, I introduced the client-side runtime, described the Microsoft AJAX Library, and explored application services in the JavaScript namespace Sys.Services—specifically, using AJAX code for authentication, roles, and profile data. In this chapter, I’ll build on these concepts and describe how to create a deployable application framework using AJAX controls and components in client-side libraries. These controls and components load data and interact with a service-oriented framework on the server.

The main goals of the AJAX JavaScript class library are portability, flexibility, maintainability, and above all, simplicity. You don’t want to use massive amounts of JavaScript, with thousands of lines in each library file. You also want to avoid single-purpose components whenever possible so that you can reuse code in multiple contexts. Finally, you don’t want to develop bloated applications that are slow to render and respond. To achieve these goals, you need to understand the limits of JavaScript and write your code appropriately.

Tip

Tip

Simplicity is a best practice in developing custom JavaScript class libraries. Keep your script library as simple as possible and use object-oriented designs to make your class interfaces simple and reusable.

The Microsoft AJAX Library provides a framework to help developers build object-oriented components for an AJAX application. This framework adds to the object-oriented JavaScript extensions to the type system that we looked at in Chapter 5. It also provides base class implementations for components, controls, and behaviors. These base classes include Sys.Component, Sys.UI.Control, and Sys.UI.Behavior. Both Sys.UI.Control and Sys.UI.Behavior are derived from Sys.Component and include the common functionality of the component infrastructure. A JavaScript component is an application object that is partially managed by the JavaScript Application object and includes base functionality such as creation and disposal services and a property and events framework. Figure 7-1 illustrates the role of the component infrastructure in an AJAX application. The component infrastructure is built into the Microsoft AJAX Library and is implemented through custom AJAX frameworks.

The component infrastructure is built into the AJAX library and implemented in custom AJAX frameworks.

Figure 7-1. The component infrastructure is built into the AJAX library and implemented in custom AJAX frameworks.

Understanding Properties and Events in the AJAX Library

Before looking further into components and controls, I’ll describe the fundamentals of properties and events in the AJAX library. After reviewing properties and events, I’ll discuss how they are implemented in AJAX library components and how to use components to make AJAX applications more robust and maintainable. In the next chapter, we’ll build on this foundation to develop controls and behaviors.

Properties

Properties are defined through a naming convention of the AJAX library and are not part of the JavaScript language. To define a property, use the get_ and set_ prefixes with the property name to define the property’s function. For example, the following code block defines the property text in the PropertyExample class and creates an instance of the class before using the text property.

Type.registerNamespace("SOAjax.Examples");

SOAjax.Examples.PropertyExample = function() { }
SOAjax.Examples.PropertyExample.prototype = {
    _text: null,
    get_text: function() {
        /// <value>Used to get or set the text of the PropertyExample.</value>
        return this._text;
    },
    set_text: function(text) {this._text = text;}
}
var myObject = new SOAjax.Examples.PropertyExample();
myObject.set_text('Hello, World!'),
Sys.Debug.trace( myObject.get_text() );

With this convention, the text property is defined, but it is not accessed through the syntax myObject.text. Instead, you gain access to the text property by using the statement myObject.get_text. A property should be documented inside the get_ property method. The documentation is used to build Visual Studio IntelliSense for the property.

When you work with properties in the AJAX library to develop components, behaviors, or controls, you refer to the property using its name, and the AJAX library looks for the property’s get_ and set_ methods. This mechanism applies to custom components as well as to components built into the AJAX library’s base framework and the AJAX Control Toolkit on Codeplex.

Tip

Tip

Properties should be defined by creating accessor methods using lowercase field names with get_ and set_ prefixes. Private fields are used to store the property field and are defined through a naming convention that uses the underscore prefix. For example, the property text would be implemented by the private field _text that is accessed through the accessor methods get_text and set_text.

Events

Events play a key role in the AJAX library and enable the development of loosely coupled JavaScript component frameworks. Throughout this book, you’ve seen examples of using events with the Sys.Application object, including the load, init, and dispose events. Events can also be based on DOM events and user actions such as mouse events and keyboard events, as you saw in Chapter 6, with the $addHandler function. You can define custom events in a JavaScript class by creating an event handler list and event binding methods using the AJAX library’s naming conventions.

To support events, you must include an instance of Sys.EventHandlerList in your class. By convention, you need to include the event handler list in a get_events property method. The following code can be used to include the event handler list in a get_events property:

get_events: function() {
    if (!this._events) {
        this._events = new Sys.EventHandlerList();
    }
    return this._events;
}

To define an event, you must create add_ and remove_ methods to enable event binding, and you must call the event handler when the event fires. The addHandler and remove-Handler methods of Sys.EventHandlerList are used to add and remove event bindings. The EventHandlerList class is used to aggregate event handlers into a single function so that multiple functions can be bound to a single event.

Tip

Tip

The EventHandlerList class is how the Sys.Application object supports multiple handlers for the init and load events.

To better understand the event handler list, refer to the AJAX library source code, which is shown in part in Example 7-1. The list handler implements a simple dictionary of event names and functions. The getHandler method returns a dynamically created aggregate function that calls multiple handler functions for the event.

Example 7-1. The Sys.EventHandlerList class defines a dictionary of events and functions and supports the Microsoft AJAX Library event framework (excerpt from MicrosoftAjax.debug.js).

Sys.EventHandlerList = function Sys$EventHandlerList() {
    this._list = {};
}
function Sys$EventHandlerList$addHandler(id, handler) {
    Array.add(this._getEvent(id, true), handler);
}
function Sys$EventHandlerList$removeHandler(id, handler) {
    var evt = this._getEvent(id);
    if (!evt) return;
    Array.remove(evt, handler);
}
function Sys$EventHandlerList$getHandler(id) {
    var evt = this._getEvent(id);
    if (!evt || (evt.length === 0)) return null;
    evt = Array.clone(evt);
    return function(source, args) {
        for (var i = 0, l = evt.length; i < l; i++) {
            evt[i](source, args);
        }
    };
}
function Sys$EventHandlerList$_getEvent(id, create) {
    if (!this._list[id]) {
        if (!create) return null;
        this._list[id] = [];
    }
    return this._list[id];
}
Sys.EventHandlerList.prototype = {
    addHandler: Sys$EventHandlerList$addHandler,
    removeHandler: Sys$EventHandlerList$removeHandler,
    getHandler: Sys$EventHandlerList$getHandler,
    _getEvent: Sys$EventHandlerList$_getEvent
}
Sys.EventHandlerList.registerClass('Sys.EventHandlerList'),

To include support for events in your class, create an events property that returns the Sys. EventHandlerList reference for the class and include add_ and remove_ methods for each supported event. For example, to define a fire event, include the methods add_fire and remove_fire, as shown in the following example:

add_fire: function(handler) {
    /// <summary>Adds a handler to the fire event.</summary>
    this.get_events().addHandler("fire", handler);
},
remove_fire: function(handler) {
    this.get_events().removeHandler("fire", handler);
}

To provide Visual Studio IntelliSense support for an event, add a summary of the event to the add method as the example demonstrates. Documentation is not needed for the remove method.

To fire the event from your class, get a handler method from the event handler list and call it. The getHandler method of the EventHandlerList will return a dynamically created method that will call each event handler that is added to the event. For example, the following code gets the handler for the fire event and calls it. Event handlers that were added to the class’s fire event will be called from the method that is returned from getHandler. Notice the null reference checks in the code—because the event handler reference is lazily created, this._events will be null if there is no event.

fireEvent: function() {
    if (this._events) {
        var handler = this._events.getHandler("fire");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    }
}

Putting it all together, Example 7-2 contains the full sample class for event binding. Take note of the internal reference of the event handler list and the add_ and remove_ methods for the fire event.

Tip

Tip

Code for this book is available online at http://www.microsoft.com/mspress/companion/9780735625914. The code for this chapter is in the file Chapter 7.zip.

Example 7-2. Events are bound through add_ and remove_ methods and are implemented by using the Sys.EventHandlerList class (Web/Script/Examples.js).

Type.registerNamespace("SOAjax.Examples");

SOAjax.Examples.EventExample = function() { }
SOAjax.Examples.EventExample.prototype = {
    /// <summary>
    /// An example of a class that demonstrates events.
    /// </summary>

    _text: null,
    _events: null,
    get_text: function() { return this._text; },
    set_text: function(text) { this._text = text; },

    get_events: function() {
        if (!this._events) {
            this._events = new Sys.EventHandlerList();
        }
        return this._events;
    },

    add_fire: function(handler) {
        /// <summary>Adds a handler to the example 'fire' event.</summary>
        this.get_events().addHandler("fire", handler);
    },
    remove_fire: function(handler) {
        this.get_events().removeHandler("fire", handler);
    },
    fireEvent: function() {
        if (this._events) {
            var handler = this._events.getHandler("fire");
            if (handler) {
                handler(this, Sys.EventArgs.Empty);
            }
        }
    }
}

Events are useful for building loosely coupled JavaScript components through handler functions in composite components or page implementations. To bind to the fire event, for example, create a method that handles the event in the page. Event handlers are passed the sender of the event plus any arguments. In the example in Example 7-2, the Sys.EventArgs. Empty reference is used, specifying no arguments. The following code defines an event handler method that is bound to an instance of the EventExample class by adding the onFire handler method with the EventExample.add_fire method. The fireEvent method is then called, which fires the event, which is in turn handled by the onFire handler.

function onFire(sender, eventArgs){
    alert('Fire!'),
}
var eventDemo = new SOAjax.Examples.EventExample();
eventDemo.add_fire(onFire);
eventDemo.fireEvent();

Custom event arguments can be used by your class by deriving from Sys.EventArgs. You might want to create a custom event arguments class to contain additional information. Example 7-3 demonstrates a simple event arguments class that defines a text property.

Example 7-3. A custom event arguments class can be used to send additional information with the event (Web/Script/Examples.js).

SOAjax.Examples.TextEventArgs = function (text) {
    SOAjax.Examples.TextEventArgs.initializeBase(this);
    this._text = text;
}
SOAjax.Examples.TextEventArgs.prototype = {
    get_text: function() { return this._text; },
    set_text: function(val) { this._text = val; }
}
SOAjax.Examples.TextEventArgs.registerClass(
    'SOAjax.Examples.TextEventArgs', Sys.EventArgs);

Example 7-4 demonstrates the use of the SOAjax.Examples.TextEventArgs class integrated with the previous sample code. You can use a custom event arguments class to pass data between components for loosely coupled, event-driven application objects.

Example 7-4. Custom event argument classes can be implemented to handle additional information in the event handlers (Web/Script/Examples.js).

SOAjax.Examples.EventExample = function() { }
SOAjax.Examples.EventExample.prototype = {
    /// <summary>
    /// An example class that demonstrates events.
    /// </summary>

    _text: null,
    _events: null,
    get_text: function() { return this._text; },
    set_text: function(text) { this._text = text; },

    get_events: function() {
        if (!this._events) {
            this._events = new Sys.EventHandlerList();
        }
        return this._events;
    },

    add_fire: function(handler) {
        /// <summary>Adds a handler to the example 'fire' event.</summary>
        this.get_events().addHandler("fire", handler);
    },
    remove_fire: function(handler) {
        this.get_events().removeHandler("fire", handler);
    },
    fireEvent: function() {
        if (this._events) {
            var handler = this._events.getHandler("fire");
            if (handler) {
                // handler(this, Sys.EventArgs.Empty);
                handler(this, new SOAjax.Examples.TextEventArgs(this.get_text()));
            }
        }
    }
}

function onFire(sender, eventArgs){
    Sys.Debug.assert(typeof(eventArgs.get_text)=='function',
        'Expected a get_text method of the event args class!')
    var text = eventArgs.get_text();
    Sys.Debug.assert(text != null, 'Expected text in the event args!'),
    alert(text);
}

// Create an event demo object and exercise it:
var eventDemo = new SOAjax.Examples.EventExample();
eventDemo.set_text('Hello, AJAX nation!'),
eventDemo.add_fire(onFire);
eventDemo.fireEvent();

While events and properties are useful in any JavaScript class, they are central to the AJAX component framework and are simplified in the Sys.Component base class by the base implementation. The Sys.Component base class contains the EventHandlerList class and the get_events method and contains a creation method that supports simple property and event binding. The Component class provides this infrastructure, which you would have to write yourself in custom classes, making the Component class an ideal base class for AJAX classes. In the next section, we’ll look at component development using Sys.Component.

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

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