0
votes

I have a simple viewmodel with an observalbe array of items, and an observable holding the selected item. I subscribe to the changes of selected item, and I can see in my tests that the handler is fired even when I assign the same value again and again, so there should not be any change. The following code shows 3 alerts with all the same "changed to ..." text.

view.SelectedItem(view.Items()[0]);
view.SelectedItem.subscribe(function(newValue) {
   alert("changed to " + ko.toJSON(newValue));
});
view.SelectedItem(view.Items()[0]);
view.SelectedItem(view.Items()[0]);
view.SelectedItem(view.Items()[0]);

Here is a demo fiddle.

3

3 Answers

1
votes

Apparently, selecting an item, even if it's the same one as what's already selected, triggers the change event, calling the function specified when subscribing.

If you want to be notified of the value of an observable before it is about to be changed, you can subscribe to the beforeChange event. For example:

view.SelectedItem.subscribe(function(oldValue) {
    alert("The previous value is " + oldValue);
}, null, "beforeChange");

Source

This could help you determine whether or not the value has changed.

1
votes

You can create function to have access to old and new values for compare it:

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

You could add this function to some file with you ko extensions. I once found it on stackoverflow but can't remeber link now. And then you could use it like this:

view.SelectedItem.subscribeChanged(function(newValue, oldValue) {
    if (newValue.Name != oldValue.Name || newValue.Quantity != oldValue.Quantity) {
        alert("changed to " + ko.toJSON(newValue));
    }   
});

Fiddle

0
votes

I ended up creating my own method based on a thread on a forum:

// Accepts a function(oldValue, newValue) callback, and triggers it only when a real change happend
ko.subscribable.fn.onChanged = function (callback) {
  if (!this.previousValueSubscription) {
    this.previousValueSubscription = this.subscribe(function (_previousValue) {
      this.previousValue = _previousValue;
    }, this, 'beforeChange');
  }
  return this.subscribe(function (latestValue) {
    if (this.previousValue === latestValue) return;
    callback(this.previousValue, latestValue);
  }, this);
};