Command Pattern with jQuery.Deferred & Knockout

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


The command pattern is a design pattern that encapsulates all the information required to perform an operation in a new object, allowing that operation to be performed later.  Working in WPF using the MVVM pattern it is almost impossible to get away from commands and the ICommand interface, so when I started writing view models in knockout that had to perform actions I started to miss the commands quite quickly.

Whenever I wanted to do something simple, like make an AJAX call…

var ViewModel = function () {
	this.doSomethingOnTheServer = function () {
		$.ajax(/*...*/);
	}
};

…I would decide to notify the user that the operation was processing…

var ViewModel = function () {
	var _self = this;
	this.isRunning = ko.observable(false);
	this.doSomethingOnTheServer = function () {
		_self.isRunning(true);
		$.ajax(/*...*/)
		.always(function() { _self.isRunning(false); });
	}
};

…and then to notify them if there was an error…

var ViewModel = function () {
	var _self = this;
	this.isRunning = ko.observable(false);
	this.errorMessage = ko.observable();
	this.doSomethingOnTheServer = function () {
		_self.isRunning(true);
		_self.errorMessage("");
		$.ajax(/*...*/)
		    .always(function () { _self.isRunning(false); })
		    .fail(function (_, message) { _self.errorMessage(message); });
	}
};

…and before long my view model was becoming unmanageably large.

Enter the Command

Instead of writing a view model a thousand lines long I decided to encapsulate all of that boilerplate code in a nice new object: Command

var ViewModel = function () {
	this.doSomethingOnTheServer = new Command({
		action: function () {
			return $.ajax(/*...*/);
		},
		done: function (data) {
			//...
		}
	});
};

var vm = new ViewModel();
vm.doSomethingOnTheServer.execute();

Note: because my commands in knockout are invariably AJAX I have made it a requirement that the ‘action’ of the command always return a jQuery.Deferred object.

So what are we doing here?

Notification Properties

Our view model needs to have 2 properties to store the status of the operation: isRunning and errorMessage. I could add a hasError flag for completeness, but the absence of an error message can be used to infer the absence of an error.

We can create these using normal knockout observable properties:

var Command = function () {
	var _self = this,

	//flag to indicate that the operation is running
	_isRunning = ko.observable(false),

	//property to save the error message
	_errorMessage = ko.observable();

	//public properties
	this.isRunning = _isRunning;
	this.errorMessage = _errorMessage;
};

The Action

When we create a command we will need to specify the action that will be performed. Let’s pass this in as a constructor parameter, and throw an error nice and early if no action has been set:

var Command = function (options) {
	//check an action was specified
	if (!options.action) throw "No action was specified in the options";

	//... rest unchanged ...

};

Now that we have an action we can start to implement the execute method that will do the work. This method needs to:

  1. Set isRunning to true and clear any old error message
  2. Invoke the action from the constructor options
  3. Check that the action function has returned a Deferred object, and attach appropriate event handlers:
    • Always set isRunning back to false
    • If the operation failed, set the errorMessage property
var Command = function (options) {
	//...
	var _execute = function () {
		//notify that we are running and clear any existing error message
		_isRunning(true);
		_errorMessage("");

		//invoke the action and get a reference to the deferred object
		var promise = options.action.apply(this, arguments);

		//check that the returned object *is* a deferred object
		if (!promise || !promise.done || !promise.always || !promise.fail)
			throw "Specified action did not return a promise";

		//set up our callbacks:
		promise
		//always notify that the operation is complete
			.always(function () { _isRunning(false); })
		//save the error message if there is one
			.fail(function (_, message) { _errorMessage(message); });
	};

	//...
	this.execute = _execute;
};

Note: I am using apply to call the action method (instead of calling it directly) as it allows us to pass parameters if needed.

Completed Handlers

So far so good, but it’s rare that you don’t want to do something more than just notify the user when an operation completes. Let’s add the ability to pass in success and failure event handlers on the constructor options:

var Command = function (options) {
	var _execute = function () {
		//...as before...

		//attach any success or failure handlers
		if (options.done) promise.done(options.done);
		if (options.fail) promise.fail(options.fail);
	};
};

Note: as we are using the jQuery Deferred object to attach the event handlers they will automatically be passed any relevant arguments (e.g. AJAX data, error messages etc) so we don’t have to do any extra work here.

Fin

And that’s it. The full source for the Command is:

var Command = function (options) {
	//check an action was specified
	if (!options) throw "No options were specified";
	if (!options.action) throw "No action was specified in the options";

	var _self = this,

	//flag to indicate that the operation is running
	_isRunning = ko.observable(false),

	//property to save the error message
	_errorMessage = ko.observable(),

	//execute function
	_execute = function () {
		//notify that we are running and clear any existing error message
		_isRunning(true);
		_errorMessage("");

		//invoke the action and get a reference to the deferred object
		var promise = options.action.apply(this, arguments);

		//check that the returned object *is* a deferred object
		if (!promise || !promise.done || !promise.always || !promise.fail)
			throw "Specified action did not return a promise";

		//set up our callbacks
		promise
		//always notify that the operation is complete
			.always(function () { _isRunning(false); })
		//save the error message if there is one
			.fail(function (_, message) { _errorMessage(message); });

		//attach any success or failure handlers
		if (options.done) promise.done(options.done);
		if (options.fail) promise.fail(options.fail);
	};

	//public properties
	this.isRunning = _isRunning;
	this.errorMessage = _errorMessage;
	this.execute = _execute;
};

The source is also available on github along with unit tests

Advertisements

‘Loading’ Placeholders using Knockout JS

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


Whenever you have an asynchronous call to the server from a web page it is good practice to have some form of loading indicator to let the user know that something is happening in the background.

Examples of loading images from around the web

(samples taken from Facebook, Amazon & Windows Azure Dashboard)

On a recent project I found myself constantly repeating code to hide areas of the UI whilst running an operation in the background and insert a ‘Loading…’ animation, so instead of repeating the same boilerplate I decided to write a re-usable custom binding to get the same effect.

Ideally, all I want to do is to specify a boolean property that indicates when the ‘Loading’ animation should be visible:

<div data-bind="loadingWhen: isLoading"></div>

The Custom Binding

Custom bindings in knockout are made up of two functions – init and update – that are called when the binding is first initialised and when its value is updated respectively.  Each function has access to both the element on which it has been set and the current value of the flag, so we have all the information we need.

The binding works by appending a new <div /> element to the container on which it is set when it is initialized.  The new div:

  1. Is absolutely positioned (centered) on the parent
  2. Has a spinner gif as a background image (I recommend the excellent ajaxload to generate something suitable)
  3. Is hidden by default

This div can then be shown/hidden along with the original content when the value of the isLoading flag changes.

Init

In the initialize step we want the create the ‘loader’ div and insert it into the parent element.  To do that we can use some basic jQuery to instantiate the div and set up the required CSS properties:

ko.bindingHandlers.loadingWhen = {
	init: function (element) {
		var
		//cache a reference to the element as we use it multiple times below
		$element = $(element),
		//get the current value of the css 'position' property
		currentPosition = $element.css("position")
		//create the new div with the 'loader' class and hide it
		$loader = $("<div></div>").addClass("loader").hide();

		//add the loader div to the original element
		$element.append($loader);

		//make sure that we can absolutely position the loader against the original element
		if (currentPosition == "auto" || currentPosition == "static")
			$element.css("position", "relative");

		//center the loader
		$loader.css({
			position: "absolute",
			top: "50%",
			left: "50%",
			"margin-left": -($loader.width() / 2) + "px",
			"margin-top": -($loader.height() / 2) + "px"
		});
	}
}

This is all fairly straightforward, though it is worth noting the setting of the CSS position on the original element.  This is because we need to use absolute positioning on the loader div so that it appears centered over the original content, and absolute positioning always works relative to the first ancestor element that does not have static positioning.  We want the loader position relative to the original element, so we need to set it’s position to make sure it is used.
Relative positioning means relative to the elements original position, so we should be able to safely replace either auto or static with relative without any offsets to have it remain in the original location.
The loader itself is given top and left margins of 50% (meaning 50% of the parent element), and then negative margins of half of the loader’s width and height so that it is properly centered.

Update

Next up we need to show and hide the content based on the value of the flag. For this we will add an update method to the binding that handles the hiding and showing using jQuery.

ko.bindingHandlers.loadingWhen = {
	init: function (element) {
		//unchanged
	},
	update: function (element, valueAccessor) {
		var
		//unwrap the value of the flag using knockout utilities
		isLoading = ko.utils.unwrapObservable(valueAccessor()),

		//get a reference to the parent element
		$element = $(element),

		//get a reference to the loader
		$loader = $element.find("div.loader")

		//get a reference to every *other* element
		$childrenToHide = $element.children(":not(div.loader)");

		//if we are currently loading...
		if (isLoading) {
			//...hide and disable the children...
			$childrenToHide.css("visibility", "hidden").attr("disabled", "disabled");
			//...and show the loader
			$loader.show();
		}
		else {
			//otherwise, fade out the loader
			$loader.fadeOut("fast");
			//and re-display and enable the children
			$childrenToHide.css("visibility", "visible").removeAttr("disabled");
		}
	}
};

Things to note here are:

  • We are using visibility: hidden instead of fadeOut as we don’t want to remove the element from the DOM; we just want to hide it.  If we removed it then the size of the parent element might change, which could cause the loader to jump about
  • We are disabling the original content to prevent it responding to any clicks etc whilst hidden

Done

And that’s all there is to it! Knockout handles the notification of changes to the isLoading flag so we should now be able to drop this into our website.

You can see a working example of this here.

Loading MVC PartialViews asynchronously using KnockoutJS

This question on StackOverflow asks (indirectly) how to use KnockoutJS to asynchronously load the result of a PartialView call into a div using jQuery.ajax.

The poster’s original attempt specified a getHtml functionon the view model, and then used the html binding to set the getHtml function. Unfortunately this cannot work as getHtml makes an asynchronous call to the server and so cannot return a value for the knockout binding to use.

One way (of many) that you could work around this would be to create a custom binding as demonstrated here.

This works by taking 2 parameters to the asyncHtml: a function to call that takes a success callback as its final parameter (plus any other parameters) and an array of the parameters that need to be passed to that function.

<div data-bind="asyncHtml: { source: getHtml, params: [123, 456] }"></div>

The custom binding then grabs these values, concats a custom callback onto the parameters that are passed to it, and calls the specified function:

ko.bindingHandlers.asyncHtml = {
    init: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var parameters = value.params.concat([function(data) {
           $(element).html(data); 
        }]);
        value.source.apply(null, parameters);
    }
};

Finally we can re-implement our view model HTML-retrieving method to make the POST call and invoke the new success handler:

var ViewModel = function() {
    this.getHtml = function(param1, param2, callback) {
        $.ajax(
            {
                type: "POST",
                url: "/echo/html/",
                data: {
                        html: "<p>server response " + param1 + " " + param2 + "</p>",
                        delay: 1
                    },
                success: callback,
                dataType: "text"
            });            
    };
};

This approach means we can flexibly call any method on our view model that wants to make an async call and then insert the result back into the view.

Binding to invalid property names with $data in KnockoutJS

A quick tip when working with KnockoutJS: if you are binding a value to a property with a valid name then it is very simple to get at the value in your HTML…

ViewModel = function() {
	this.value = ko.observable("Hello World");
};

ko.applyBindings(new ViewModel());
...

<div data-bind="text: value"></div>

But things are not quite as simple when you have a property value that has a space (or other invalid character) in the name:

ViewModel = function() {
	this["value with space"] = ko.observable("Hello World");
};

To bind to this value, we need to use the $data variable that for some reason is not really covered in the Knockout documentation (though it is mentioned here).

The $data variable will always return the object that is currently being bound (think DataContext for any WPF developers), and we can use it to access the value of our property through the indexer as we would in vanilla JS:

<div data-bind="text: $data['value with space']()"></div>