Chris Pietschmann

husband, father, hacker, entrepreneur, futurist, innovator, autodidact

NAVIGATION - SEARCH

ASP.NET AJAX: Create a JavaScript Component with Events

In the spirit of my "Mixing OOP and Event Driving Programming" article I posted a couple days ago; this article discusses how to implement the same thing using the ASP.NET AJAX JavaScript Library. Using inheritence (the ASP.NET AJAX way) it is even easier to implement a little Event Driven Model into your client-side, JavaScript classes/components.

Event Driven Basics

First we'll start by creating a Person class that inherits from the Sys.Component class. Now remember, since we are doing things the ASP.NET AJAX way, we aren't using JavaScript's Prototypal Inheritence.

Person = function() {
    Person.initializeBase(this);
};
Person.prototype = {
    initialize: function() {
        Person.callBaseMethod(this, "initialize");
   },
    dispose: function() {
        Person.callBaseMethod(this, "dispose");
    }
};
Person.registerClass("Person", Sys.Component);

At first glance you may notice that there's a little more plumbing required to inherit from another object using ASP.NET AJAX. This is true, however it does make implementing Events much easier. The magic of inheritence mostly occurs within the call to the "registerClass" method that actually sets up the class/object to inherit from "Sys.Component." It also sets up the "initialize" method to be called when the object is instantiated.

If you have question (and I'm sure you do if you're not familiar with ASP.NET AJAX), please go check out the ASP.NET AJAX Documentation, as further details aren't within the scope of this article.

Now, the hash table that we implemented in the previous article is already implemented for us within the "Sys.Component" class. We can access it using the "private" "get_events" method. The "get_events" method actually returns an instance of the"Sys.EventHandlerList" class that also implements the "getHandler" method for us. This all makes implementing Events much simpler.

First, lets create a "raiseEvent" method, since this isn't created for us. This method will allow us to actually Raise the Events with only a single method call.

Person.prototype = {
    raiseEvent: function(eventName, eventArgs) {
        var handler = this.get_events().getHandler(eventName);
        if (handler) {
            if (!eventArgs) {
                eventArgs = Sys.EventArgs.Empty;
             }
            handler(this, eventArgs);
        }
    }
};

Now lets add the "Name" property (with accessors) to our Person class, and have the "set" accessor Raise the "onchangename" event:

Person.prototype = {
    get_Name: function() {
        return this._name;
    },
    set_Name: function(value) {
        // get old value
        var oldValue = this._name;

        // set new value
        this._name = value;

        // Raise the Event and send the old value as the "EventArgs"
       this.raiseEvent("onchangename", oldValue);
   }
};

Here's an example of using the Person class, and attaching a function to handle the "onchangename" event:

var p = new Person();

p.get_events().addHandler(
        "onchangename",
        function(sender, eventArgs) {
            alert("Old Value: " + eventArgs);
        }
    );

p.set_Name("Charlie");

As you can see in the example event handler that we added above, the function gets passed two parameters (sender and eventArgs). The sender is the Person object that is raising the event, and the eventArgs is this events arguments. So far we are only passing the old Name value as the event arguments, but in the next section of the article we'll see how to create our own EventArgs object that inherits from "Sys.EventArgs" that can contain anything we want to pass to the event handler.

Go Further With Sys.EventArgs

The proper way to raise events in ASP.NET AJAX, is to pass an instance of "Sys.EventArgs" when raising events. This allows you to pass any event arguments to the event handler that you want/need. This also follows suit after the way Events are handled within the .NET Framework.

To do this, we'll first create our own custom EventArgs class that inherits from "Sys.EventArgs", and we'll add "OldName" and "NewName" read-only properties to it (to do this we'll only add 'get' accessors). We'll set the "OldName" and "NewName" properties within the constructor, so we'll make the constructor accept those as well.

PersonEventArgs = function(oldNameValue, newNameValue) {
    this._oldName = oldNameValue;
    this._newName = newNameValue;
};
PersonEventArgs.prototype = {
    get_OldName: function() {
        return this._oldName;
    },
    get_NewName: function() {
        return this._newName;
    }
};
PersonEventArgs.registerClass("PersonEventArgs", Sys.EventArgs);

Now to make use of this new PersonEventArgs class, we just need to change the line of code within the Person class's "Name" properties accessor to the following:

this.raiseEvent("onchangename", new PersonEventArgs(oldValue, this._name));

Now to access the "OldName" and "NewName" properties of the PersonEventArgs when they are passed to the "onchangename" event handler, just call their property accessors. Like the following:

var p = new Person();
p.get_events().addHandler(
    "onchangename",
    function(sender, eventArgs) {
        alert("Old Name: " + eventArgs.get_OldName();
        alert("New Name: " + eventArgs.get_NewName();
   });

Simplify the Adding of Event Handlers

Having to call the "get_events" method, the calling the "addHandler" method and passing it the name of the event can be a little cumbersome. You can create "add_{eventName}" methods that accept one argument (the event handler method) that will add the event handler for you with only one method call, and never needing to pass (and type or misstype) the event name. This is actually how the objects within the ASP.NET AJAX JavaScript library handle adding events. They also implement a "remove_eventName" method that does the same, but removes the event handler passed in.

Person.prototype = {
    add_changeName: function(handler) {
        this.get_events().addHandler("onchangename", handler);
    },
    remove_changeName: function(handler) {
        this.get_events().removeHandler("onchangename", handler);
    }
};

If you compare the below example of adding an event handler for the "onchangename" event, you'll see that it is much simpler to call these methods than the example above.

var p = new Person();
p.add_changeName(function(sender, eventArgs) {
        alert("Old Name: " + eventArgs.get_OldName();
        alert("New Name: " + eventArgs.get_NewName();
   });

Complete Source Code

Heres' the complete code for the Person object created with this article.

First, the example usage code:

var p = new Person();

p.add_changeName(function(sender, eventArgs) {
    alert("Old Name: " + eventArgs.get_OldName());
    //alert("New Name: " + eventArgs.get_NewName());
});

p.set_Name("Charlie");

p.set_Name("Chris");

And, now the full Person and PersonEventArgs code:

Person = function() {
    Person.initializeBase(this);
    this._name = null;
};
Person.prototype = {
    initialize: function() {
        Person.callBaseMethod(this, "initialize");
    },
    dispose: function() {
        Person.callBaseMethod(this, "dispose");
    },

    raiseEvent: function(eventName, eventArgs) {
        var handler = this.get_events().getHandler(eventName);
        if (handler) {
            if (!eventArgs) {
                eventArgs = Sys.EventArgs.Empty;
            }
            handler(this, eventArgs);
        }
    },

    // Property Accessors
    get_Name: function() {
        return this._name;
    },
    set_Name: function(value) {
        // get old value
        var oldValue = this._name;

        // set new value
        this._name = value;

        // Raise the Event and send the old value as the "EventArgs"
        this.raiseEvent("onchangename", new PersonEventArgs(oldValue, this._name));
    },

    add_changeName: function(handler) {
        this.get_events().addHandler("onchangename", handler);
    },
    remove_changeName: function(handler) {
        this.get_events().removeHandler("onchangename", handler);
    }
};
Person.registerClass("Person", Sys.Component);

PersonEventArgs = function(oldNameValue, newNameValue) {
    this._oldName = oldNameValue;
    this._newName = newNameValue;
};
PersonEventArgs.prototype = {
    get_OldName: function() {
        return this._oldName;
    },
    get_NewName: function() {
        return this._newName;
    }
};
PersonEventArgs.registerClass("PersonEventArgs", Sys.EventArgs);

Conclusion

As you can see, it's event easier to add/handle events with your custom classes when you use the ASP.NET AJAX library, rather than implementing it completely yourself. Either way you can use inheritence to greatly simplify things.

blog comments powered by Disqus