Get previous value of an observable in subscribe of same observable
Asked Answered
A

5

87

Is it possible in knockout to get the current value of an observable within a subscription to that observable, before it receives the new value?

Example:

this.myObservable = ko.observable();
this.myObservable.subscribe(function(newValue){
    //I'd like to get the previous value of 'myObservable' here before it's set to newValue
});
Arissa answered 10/10, 2012 at 15:25 Comment(0)
I
92

There is a way to do a subscription to the before value like this:

this.myObservable = ko.observable();
this.myObservable.subscribe(function(previousValue){
    //I'd like to get the previous value of 'myObservable' here before it's set to newValue
}, this, "beforeChange");
Illstarred answered 10/10, 2012 at 16:24 Comment(2)
what does this stands for here?Womanly
@ThanasisIoannidis It ensures that you can access the same value of "this" inside the callback function. Otherwise you would need to save it to another name beforehand, e.g. var self = this; (which is a perfectly valid strategy too).Koser
B
154
ko.subscribable.fn.subscribeChanged = function (callback) {
    var oldValue;
    this.subscribe(function (_oldValue) {
        oldValue = _oldValue;
    }, this, 'beforeChange');

    this.subscribe(function (newValue) {
        callback(newValue, oldValue);
    });
};

Use the above like this:

MyViewModel.MyObservableProperty.subscribeChanged(function (newValue, oldValue) {

});
Bimah answered 12/8, 2013 at 9:49 Comment(6)
fairly new to knockout, but I'm wishing this is the way the default subscribe was setup. Or.. this fn will at least scratch my first itch as I use 'subscribe' for the first time.Berghoff
There has been some movement on this on github.com/knockout/knockout/issues/914. Looks like it's slated for the 3.4 release.Grantland
In case the subscribed observable value type is an Array, you have to slice it, otherwise the oldValue will always be the same as the newValue. Check a working example, here: jsfiddle.net/david_freire/xmk6u9yn/4Tithing
Cool. Added a return value which is a subscription object with a dispose() function gist.github.com/30ff1f5c1adf215179b0046515f86e45Peanut
Oh just saw the git conversation.Peanut
@DavidFreire if the array contains objects it's still broken. I tried to work around it, it's quite messy. In the end I added an additional subscription that handles the value as JSON completely.Adverbial
I
92

There is a way to do a subscription to the before value like this:

this.myObservable = ko.observable();
this.myObservable.subscribe(function(previousValue){
    //I'd like to get the previous value of 'myObservable' here before it's set to newValue
}, this, "beforeChange");
Illstarred answered 10/10, 2012 at 16:24 Comment(2)
what does this stands for here?Womanly
@ThanasisIoannidis It ensures that you can access the same value of "this" inside the callback function. Otherwise you would need to save it to another name beforehand, e.g. var self = this; (which is a perfectly valid strategy too).Koser
L
23

Little change to Beagle90 answer. Always return the subscription itself to be able to access the dispose() for instance.

ko.subscribable.fn.subscribeChanged = function (callback) {
    var oldValue;
    this.subscribe(function (_oldValue) {
        oldValue = _oldValue;
    }, this, 'beforeChange');

    var subscription = this.subscribe(function (newValue) {
        callback(newValue, oldValue);
    });

    // always return subscription
    return subscription;
};
Loverly answered 22/12, 2014 at 9:35 Comment(1)
This is a real step up, but calling .dispose on the return value from this will only dispose of the second subscription, not the 'beforeChange' subscriptionKaree
M
18

The pull request to add this feature has some different code that winds up being better than relying on using the beforeChange event.

All credit for the solution to Michael Best

ko.subscribable.fn.subscribeChanged = function (callback) {
    var savedValue = this.peek();
    return this.subscribe(function (latestValue) {
        var oldValue = savedValue;
        savedValue = latestValue;
        callback(latestValue, oldValue);
    });
};

To quote Michael:

I originally suggested using beforeChange to solve this problem but have since realized that it's not always reliable (for example, if you call valueHasMutated() on the observable).

Murrumbidgee answered 5/3, 2015 at 23:55 Comment(0)
C
4

I have found that I can call peek() from a writable computed observable to get the before value.

Something like this (see http://jsfiddle.net/4MUWp):

var enclosedObservable = ko.observable();
this.myObservable = ko.computed({
    read: enclosedObservable,
    write: function (newValue) {
        var oldValue = enclosedObservable.peek();
        alert(oldValue);
        enclosedObservable(newValue);
    }
});
Codger answered 8/4, 2013 at 15:36 Comment(3)
That doesn't work, unfortunately, since by the time the subscribe callback is called, the value already changed and so peek() will give you the new value.Mickimickie
@MichaelTeper I know I posted my answer a year ago, but after I got some downvotes, I've just tested it, and it does work. See: jsfiddle.net/4MUWpCodger
Ok I see what you did there... The question was about retrieving the value in a subscribe callback which cannot be done with peek(). Your example proves nothing and could confuse a newcomer. You basically are wrapping a private variable here, and display its value before setting it - so of course it won't have changed.Waki

© 2022 - 2024 — McMap. All rights reserved.