Microsoft Ajax Library Declarative Command Alternatives

There may be another way to accomplish this, but I wanted to have a simple commanding system in the Microsoft Ajax Library which worked outside of the templates.

After a number of false starts and frustration brought about by very limited documentation, I discovered a reasonable implementation.

I created a script file, “Commanding.js” (in a Scripts folder):

/// <reference name="MicrosoftAjax.js"/>

Type.registerNamespace("WiredPrairie");

WiredPrairie.Commanding = function(element) {
    WiredPrairie.Commanding.initializeBase(this, [element]);
}

WiredPrairie.Commanding.prototype = {
    initialize: function() {
        WiredPrairie.Commanding.callBaseMethod(this, 'initialize');

        // Add custom initialization here
    },
    dispose: function() {
        //Add custom dispose actions here
        WiredPrairie.Commanding.callBaseMethod(this, 'dispose');
    }
}

WiredPrairie.Commanding.registerClass('WiredPrairie.Commanding', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

This is just a basic JavaScript class, with the default implementation provided by the Ajax Client Control template in Visual Studio 2008. The reason this is needed is that Controls all support a special event onBubbleEvent that is needed by commands when they’re raised.

In the body of the HTML, I attached an instance of the “WiredPrairie.Commanding” class to the body. Next, I attached a JavaScript function to the onbubbleevent (wpc:onbubbleevent, remember that all attributes need to be all lowercase). Here, I’ve used the special {{ }} syntax to indicate I want JavaScript code to execute when the event is raised.

<body xmlns:sys="javascript:Sys" 
    xmlns:wpc="javascript:WiredPrairie.Commanding" 
    sys:attach="wpc" 
    wpc:onbubbleevent="{{onCommand}}" >
    <div >
        <button sys:command="startRunningCommand" sys:commandargument="iargueaboutit">Run</button>
    </div>
</body>

I needed my JavaScript WiredPrairie.Commanding class to load and run at the right time on the page, so I’ve used the new loader classes in the Ajax library.

Sys.loader.defineScripts(null, [
{
    name: "Commanding"
    ,releaseUrl: "Scripts/Commanding.js"
    ,debugUrl: "Scripts/Commanding.js"
    ,dependencies: ["ComponentModel"]
 }
 ]);

First, I needed to declare this new script file and any dependencies. Since I don’t yet have a minified version, I’ve just specified the same file for the releaseUrl and the debugUrl. For the dependencies property, I looked through a few source files to discover that the “ComponentModel” key included Sys.UI.Control, which my class depends on to be created, so I added that here (hopefully this will be documented at some point).

Next, I added code to indicate to the loader which scripts were necessary and let it determine the best way to load them:

Sys.require([Sys.components.dataView, Sys.scripts.jQuery,
Sys.scripts.Commanding]);

Here, you see the “Commanding” key is added to a special namespace, “Sys.scripts.”

In the new onReady event which is raised when the DOM and scripts have been loaded, I’ve added code to activate the control class I wrote:

Sys.onReady(function() {
    Sys.activateElements(document.documentElement);    
});

Finally, I wired up that event declared above in the onbubbleevent attribute on the body element.

function onCommand(sender, args) {
    if (typeof (args) !== "undefined") {
        var commandName = args.get_commandName();
        var commandArgument = args.get_commandArgument();
        alert(commandName + " " + commandArgument);
    }
}

OK, that’s cool, but you might want to raise a command without it being triggered by a control event (such as a click). So, I added a simple function to my Commanding class:

WiredPrairie.Commanding.raiseCommand = function(sender, commandName, commandArgument, commandSource) {
    var source = sender || document.body;
    Sys.UI.DomElement.raiseBubbleEvent(source, new Sys.CommandEventArgs(commandName, commandArgument, commandSource));
}

var $sendCommand = WiredPrairie.Commanding.raiseCommand;

Because of the way the raiseBubbleEvent works, it does depend on the source being set to a valid control/element (as raiseBubbleEvent walks through the parent chain until there aren’t any more parents) – in this case, I’ve defaulted to the body element.

Finally, an enhancement to the test case above to demonstrate both receiving and sending a command:

function onCommand(sender, args) {
    if (typeof (args) !== "undefined") {
        var commandName = args.get_commandName();
        var commandArgument = args.get_commandArgument();
        switch (commandName) {
            case "startRunningCommand":
                $sendCommand(null, "alertCommand", new Date().toLocaleTimeString(), null);
                break;
            case "alertCommand":
                alert(commandArgument);
                break;
            default:
                break;
        }
    }
}

One command just sends another command which displays an alert. It’s simple, but functional.

Enjoy.

One Comment

Comments are closed.