Question

I'm trying to create a custom binding that will show a loading gif while content is loading.

ko.bindingHandlers.loader = {
    init: function (element) {
        $('<div>').addClass('loader').hide().appendTo($(element));
    },
    update: function (element, valueAccessor) {
        var isLoading = ko.utils.unwrapObservable(valueAccessor());
        var $element = $(element);
        var $children = $element.children(':not(.loader)');
        var $loader = $(element).find('.loader');

        if(isLoading) {
            $children.stop(true).css('visibility', 'hidden').attr('disabled', 'disabled');
            $loader.stop().fadeIn();
        } else {
            $loader.stop(true).fadeOut(function () {
                $children.css('visibility', 'visible').removeAttr('disabled');
            });
        }
    }
};

I can see in the init that div.loader is being appended to the element, and can see the update function fire when isLoading is changed to true. But once the images have loaded (by loaded i mean each image returns a resolved promise on the their respective load event) I don't see the update firing once isLoading is set back to false.

viewModel

function viewModel() {
    var self = this;

    self.movies = ko.observableArray([]);
    self.searchValue = ko.observable();
    self.isLoading = ko.observable(false);

    self.search = function () {
        self.isLoading = true;
        $.getJSON(arguments[0].action, { name: this.searchValue() }, function (data) {
            self.movies(data);
            $.when.apply($, promises).done(function() {
                setThumbnailHeight(function () {
                    self.isLoading = false;
                });
            });
        });
    }; 
    var setThumbnailHeight = function(callback) {
        var $items = $('.thumbnails li');
        var maxHeight = Math.max.apply(null, $items.map(function () {
            return $(this).innerHeight();
        }).get());
        $items.css('height', maxHeight);
        callback();
    };

}

ko.applyBindings(new viewModel());

setThumbnailHeight is being called at the correct time (once all promises have resolved) and is working properly, in that I see it setting the height of each li to the max height and can see the callback (in this case function(){ self.isLoading = false; } being called.

my binding

<ul class="content thumbnails" data-bind="foreach: movies, loader: $root.isLoading">
    <li class="movie">
        ...
    </li>
</ul>

So just to recap, the problem is that the loading gif will be displayed when isLoading is set to true but is not hiding and showing the newly loaded content when it's set back to false.

Was it helpful?

Solution

All observables are function so you cannot assign value to it using =. Use self.isLoading(true); instead of self.isLoading = true;

self.search = function () {
    self.isLoading(true);
    $.getJSON(arguments[0].action, { name: this.searchValue() }, function (data) {
        self.movies(data);
        $.when.apply($, promises).done(function() {
            setThumbnailHeight(function () {
                self.isLoading(false);
            });
        });
    });
};

function(){ self.isLoading(false); }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top