1

Scenario:

I'm creating a long-running web app built on knockout.js where i need to load several times different data for my viewmodels.

To improve performace and avoid unnecessary updates, i need during the update of my viewmodels to temporarily avoid the normal dependency tracking chain for my observables and observablearrays as well. After the loading of the new data is completed, i can safely restore the normal ko dependency tracking.

To pause observables i'm using the withPausing function of the great RP Niemeyer described here: Change observable but don't notify subscribers in knockout.js.

Is there any way to temporarily ignore the removal and insertion also in a observablearray?

deblocker
  • 7,629
  • 2
  • 24
  • 59

1 Answers1

2

Inspired by implementation of pauseableComputed and observable withPausing I have created pauseableObservable and pauseableObservableArray that have abilities to stop notifications to subscribers and than resume when needed. Also it work recursively for all nested pauseable properties.

You can play with it HERE on Codepen.

PauseableObservable:

// PauseableObservable - it's observable that have functions to 'pause' and 'resume' notifications to subscribers (pause/resume work recursive for all pauseable child).

ko.isPauseableObservable = function(instance) {
    return ko.isObservable(instance) && instance.hasOwnProperty("pause");
}

ko.pauseableObservable = function(value) {
    var that = ko.observable(value);

    function getPauseableChildren() {
        var currentValue = that();
        var properties = Object.getOwnPropertyNames(currentValue);
        var pauseables = properties.filter((property) => {
            return ko.isPauseableObservable(currentValue[property]);
        });
        return pauseables.map((property) => { 
            return currentValue[property]; 
        });
    }

    that.pauseNotifications = false;
    that.isDirty = false;

    that.notifySubscribers = function() {
        if (!that.pauseNotifications) {
            ko.subscribable.fn.notifySubscribers.apply(that, arguments);
        }
        that.isDirty = that.pauseNotifications;
    };

    that.pause = function() {    
        that.pauseNotifications = true;
        var pauseableChildren = getPauseableChildren();
        pauseableChildren.forEach((child) => { child.pause(); });
    }

    that.resume = function() {    
        that.pauseNotifications = false;

        if (that.isDirty) {
            that.valueHasMutated();
        }

        var pauseableChildren = getPauseableChildren();
        pauseableChildren.forEach((child)=> { child.resume(); });
    }

    return that;
}

PauseableObservableArray

// PauseableObservableArray - it's observable array that have functions to 'pause' and 'resume' notifications to subscribers about add/remove items. 
// In case if array items pauseable observables - run recursively run 'pause'/'resume' on them.

ko.pauseableObservableArray = function(items) {
    var that = ko.observableArray(items);

    that.pauseNotifications = false;
    that.isDirty = false;
    that.lastNotification = [];

    that.notifySubscribers = function() {
        if (!that.pauseNotifications) {
            ko.subscribable.fn.notifySubscribers.apply(that, arguments);
        } else {
            that.lastNotification = arguments;
        }
        that.isDirty = that.pauseNotifications;
    };

    that.pause = function () {
        var items = that();
        that.pauseNotifications = true;
        items.forEach(function(item) {
            if(ko.isPauseableObservable(item)) {
                item.pause();
            }
        });
    }

    that.resume = function () {
        var items = that();
        that.pauseNotifications = false;

        if(that.isDirty) {
            ko.subscribable.fn.notifySubscribers.apply(that, that.lastArguments);
        } 
        items.forEach(function(item) {
            if(ko.isPauseableObservable(item)) {
                item.resume();
            }
        });
    }

    that.refresh = function () {
        that.resume();
        that.pause();
    }

    return that;
}

Usage example:

var firstItem = ko.pauseableObservable("Hello");
var secondItem = ko.pauseableObservable("World");
var items = [
    firstItem,
    secondItem
];
var array = ko.pauseableObservable(items);

// Stop notifications from specific observable
firstItem.pause();
// Change won't raise notification to subscribers
firstItem("Hi");
// Resume notifications
firstItem.resume();

// Stop notifications from all items of array
array.pause();
// Change won't raise notification to subscribers
array.push("Hey");
array()[0]("Hi");
// Resume notifications
array.resume();
mykhailo.romaniuk
  • 1,058
  • 11
  • 20