fallback-images

Fallback Images with Knockout

After a busy few weeks at work I’ve finally managed to spend some time on knockout development again, and today I found a nice solution to a problem with data-bound images.

In my example I had a list of contacts that were being displayed on the page, and each contact had a URL linking to their profile image.

{
	Id: 1,
	FirstName: "Steve",
	LastName: "Greatrex",
	ProfileImage: "/some/image.jpg"
}

The binding to display the image is initially pretty simple – I can use the attr binding to set the src property on the img element using the vanilla knockout library.

<img data-bind="attr: { src: ProfileImage }" />

Simple enough, right?

Complications

Unfortunately some of my contacts don’t have a ProfileImage property.  Even worse, some of them do have a ProfileImage but it points to a non-existant image.

If I use the attr binding as above then I get an unpleasant looking “missing image” icon…

image

…when what I really want to do is use a placeholder image instead.

The img binding

To resolve this problem I created a new custom binding named img.  This expects a parameter containing an observable src property for the image URL, and either a hard-coded or an observable fallback URL to be used in case of null URLs or errors.

<img data-bind="img: { src: ProfileImage, fallback: '/images/generic-profile.png' }" />

The binding itself is nothing complicated.  As with all custom bindings you can optionally specify an update and an init handler that are called when the value changes and when the binding is initialised respectively.

For this binding, the update handler needs to check the value of both the src and fallback properties, then set the src attribute on the img to whichever value is appropriate.

The only thing that the init function needs to handle is the scenario where the image fails to load (using jQuery.error).

ko.bindingHandlers.img = {
    update: function (element, valueAccessor) {
        //grab the value of the parameters, making sure to unwrap anything that could be observable
        var value    = ko.utils.unwrapObservable(valueAccessor()),
            src      = ko.utils.unwrapObservable(value.src),
            fallback = ko.utils.unwrapObservable(value.fallback),
            $element = $(element);

        //now set the src attribute to either the bound or the fallback value
        if (src) {
            $element.attr("src", src);
        } else {
            $element.attr("src", fallback);
        }
    },
    init: function (element, valueAccessor) {
        var $element = $(element);

        //hook up error handling that will unwrap and set the fallback value
        $element.error(function () {
            var value = ko.utils.unwrapObservable(valueAccessor()),
                fallback = ko.utils.unwrapObservable(value.fallback);

            $element.attr("src", fallback);
        });
    },
};

That’s all there is to it – you can now specify a “primary” and a “fallback” binding for your images to get something like the effect below:

image

Another problem solved by the Swiss army knife that is knockout custom bindings.

Advertisements