0
votes

It seems Knockout's convenient "afterRender" method on foreach bindings only fires when data is added, and not removed.

You can test that here - http://jsfiddle.net/farina/4BaXs/1/ by clicking load initial, seeing the four alerts, then clicking load delete and notice nothing happens.

So, to solve this problem I decided to attempt to create a manual subscription to my ko.mapping.fromJS array, but that just results in a call every time I update the data from AJAX, even if the data hasn't changed.

I feel like the mapping plugin is somehow failing, and thinking that the data is always new, even when it is not.

Is there some sort of event which only occurs when the array has changed? My subscriptions on individual items work as expected, but obviously you can't subscribe to an entire list of items?

1
I'm also noticing that computed observables get notified every time the AJAX update runs fromJS. I know at one point I had this working...I can't figure out what is causing the notifications right now. The data isn't changing! - farina
knockout also has the "beforeRemove" method for the foreach as well. Maybe when you use the two together you can get what you want. - Ryan Fiorini
The problem with "beforeRemove" is that it fires before the UI is updated. I want to subscribe to whatever Knockout uses to alert it's rendering engine. - farina
The most simple way to ask this question is "Is it possible to subscribe to a Knockout Mapping plugin "fromJS" array only to detect if the array has changed (items added or removed) and nothing more? - farina

1 Answers

1
votes

I wrote up a function that uses ko.utils.compareArrays to figure out what has changed. compareArrays is also used by the foreach binding. I also took your example and updated it to use this function. Here it is:

ko.subscribable.fn.subscribeArrayChanged = function(addCallback, deleteCallback) {
    var previousValue = undefined;
    this.subscribe(function(_previousValue) {
        previousValue = _previousValue;
    }, undefined, 'beforeChange');
    this.subscribe(function(latestValue) {
        var editScript = ko.utils.compareArrays(previousValue, latestValue);
        for (var i = 0, j = editScript.length; i < j; i++) {
            switch (editScript[i].status) {
                case "retained":
                    break;
                case "deleted":
                    if (deleteCallback)
                        deleteCallback(editScript[i].value);
                    break;
                case "added":
                    if (addCallback)
                        addCallback(editScript[i].value);
                    break;
            }
        }
        previousValue = undefined;
    });
};