With KnockoutJS, how can I display a temporary picture while an observable picture is being loaded?

StackOverflow https://stackoverflow.com/questions/15666489

Question

Imagine you have the following "light" object representing a book:

    var BookModel = function(data) {
        var self = this;
        self.id = ko.observable(data.id);
        self.title = ko.observable(data.title);
        self.pages = ko.observable(data.pages);
        self.pictureURL = ko.observable(data.pictureURL);
    };

Now, imagine you have to display a lot of books, and as such, a lot of book pictures on the screen. The pictures are not necessarily on your website and even if they are, it could take some time for them to all be displayed.

What I would like to do is to preload the picture with the given pictureURL when this specific property is updated, in order to be able to display a "lazy loading gif" instead. When the picture is preloaded, it should be displayed instead of the lazy loading gif.

How can I achieve this ?

Was it helpful?

Solution

Well, I did it with an extender the following way:

ko.extenders.preloadImage = function(target, lazyImage) {
    var preloadedImage = null;
    var lazyLoadImage = lazyImage || "img/ajax-loader.gif";

    //create a writeable computed observable to intercept writes to our observable
    var result = ko.computed({
        read: target,  //always return the original observables value
        write: function(newValue) {
            var current = target();

            if(newValue == lazyLoadImage || newValue == preloadedImage) {
                valueToWrite = newValue;
            } else {
                preloadedImage = newValue;
                valueToWrite = lazyLoadImage;

                $('<img />').attr('src', newValue).load(function() {
                    valueToWrite = newValue;
                    preloadedImage = null;

                    target(valueToWrite);
                    target.notifySubscribers(valueToWrite);
                });
            }

            //only write if it changed
            if (valueToWrite !== current) {
                target(valueToWrite);
            } else {
                //if the rounded value is the same, but a different value was written, 
                // force a notification for the current field
                if (newValue !== current) {
                    target.notifySubscribers(valueToWrite);
                }
            }
        }
    });

    //initialize with current value
    result(target());

    //return the new computed observable
    return result;
};

And you can use it the following way:

   var BookModel = function(data) {
        var self = this;
        self.id = ko.observable(data.id);
        self.title = ko.observable(data.title);
        self.pages = ko.observable(data.pages);
        self.pictureURL = ko.observable(data.pictureURL).extend({ preloadImage : null });
    };

It works with a default img/ajax-loader.gif picture or another picture you pass as the lazyImage parameter. Basically, whenever a new picture is to be displayed, the lazy loading gif is displayed instead. Only when the original picture has been preloaded by the browser does it really show on screen.

This is not much, but it may be helpful to KnockoutJS lovers out here :-) Also, if you think it could be better, please edit the code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top