Command Pattern v2 using Knockout

Update: this feature is now available as part of the ko.plus library available on GitHub and NuGet!


In an earlier post I outlined a basic implementation of a command pattern using Knockout that tracks the progress of an asynchronous operation using the jQuery.Deferred object.

Having used it for a couple of weeks I found that there were a few things that could do with improving, so here we go with Command v2!.

No more .execute()

The first version of the command would wrap the operation in an object, meaning that whenever you wanted to bind to or just run the action you had to call a .execute method on the new object:

//old code
var viewModel = {
    doSomething: new Command(function() {
        //invoke async operation
    });
};

//invoke the command
viewModel.doSomething.execute();

Whilst using the command I kept forgetting to add the .execute (particularly to bindings) and this quickly became annoying, so I changed the constructor function to return the execution function instead of a new object in a similar manner to ko.observable.

Note: having previously been returning this I had to ditch the traditional constructor for a factory method that is a bit more in line with the Knockout syntax.

ko.command = Utils.command = function (options) {
    //...

    //execute function (and return object
    var _execute = function () {
       //...
    },

    //public properties now attached to _execute instead of this
    _execute.isRunning            = _isRunning;
    _execute.done                 = _done;
    _execute.fail                 = _fail;
    _execute.always               = _always;

    return _execute;
};

After these changes the invocation of the command is much simpler:

var viewModel = {
    doSomething: ko.command(function() {
        //invoke async operation
    });
};

//invoke the command
viewModel.doSomething();

//get the status of the operation
viewModel.doSomething.isRunning();

//attach a success handler
viewModel.doSomething.done(function() {
    alert("Finished");
});

That’s much cleaner, and the helper observables and functions such as isRunning and the callback methods still function as before.

Support for canExecute

The command pattern implementation in .NET (the original inspiration for writing this) includes a CanExecute method that determines whether or not the command is currently in a suitable state to execute. For example, if the command is going to submit a form, the CanExecute implementation might check that the form is valid prior to submission.

For my JavaScript command I wanted to be able to specify a custom function to determine whether or not the command can execute, as well as always preventing execution whilst the command is running asynchronously. To do this I made use of Knockout’s computed observables to implement a canExecute property:

ko.command = Utils.command = function (options) {
    //factory method to create a $.Deferred that is already completed
    _instantDeferred = function(resolve, returnValue) {
       //
    },

    //execute function
    var _execute = function () {
        //check if we are able to execute
        if (!_canExecute()) {
            //dont attach any global handlers
            return _instantDeferred(false).promise();
        }

        //...
    },

    //dummy to allow us to force a re-evaluation of the computed _canExecute observable
    _forceRefreshCanExecute = ko.observable(),

    //canExecute flag observable
    _canExecute = ko.computed(function() {
        _forceRefreshCanExecute(); //get and ignore the value

        //can execute if we're not running, and either there's no canExecute
        //function specified or it returns true
        return !_isRunning() &&
            (typeof options.canExecute === "undefined" ||
            options.canExecute.call(_execute));
    }, _execute),

    //invalidate canExecute
    _canExecuteHasMutated = function() {
        _forceRefreshCanExecute.notifySubscribers();
    };

    //public properties
    _execute.canExecute           = _canExecute;
    _execute.canExecuteHasMutated = _canExecuteHasMutated;

    return _execute;
};

Things of interest here:

  • If the _execute method cannot execute, it returns a $.Deferred object that has already been rejected so that any calling class that attaches a done/fail handler will not throw exceptions and can at least sensibly react
  • We are creating a dummy observable (_forceRefreshCanExecute) that is used by the canExecuteHasMutated function to force the computed canExecute observable to refresh (as described here)

Synchronous Execution

The first version relied on the action returning a promise object representing the status of the background operation, but after implementing the canExecute support I started to see a use case for commands that do not do anything in the background and simply execute synchronously.

var ExampleViewModel = function(externalValidationDefinition) {
  var _self = this;
  this.listOfItems = ko.observableArray();
  this.clearList = ko.command({
    action: function() {
      _self.listOfItems.removeAll();
    },
    canExecute: function() {
      return _self.listOfItems().length > 0;
    }
  });
};

To accommodate this type of usage I have updated the behaviour of the command so that when the action returns anything other than a promise it wraps that result in an immediately-completed jQuery.Deferred instance and then executes any registered handlers.

Other Minor Changes

errorMessage Removed

I realised that the error handling in version 1 was far too dependent on the signature of the error handler - what works for a failed AJAX request will not work for a failed HTML5 SQL request etc. - so I elected to remove the built-in error message recording and leave that down to the user.

Better Error Handling

As part of the support for synchronous code I also added improved error handling so that any error thrown directly from the action will be passed to the fail handlers.

Source

As ever, the code and unit tests are available on GitHub so help yourselves.