KnockoutJS Forcing A Computed Observable to Re-Com

2019-07-06 14:48发布

问题:

I'm new to KnockoutJS and think it's a great tool so far :)! However, I've a question regarding computed observables, and it's as follows. On the KnockoutJS page at http://knockoutjs.com/documentation/computedObservables.html, there's a sample code for formatting the price. The code is as follows.

HTML
<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>

And the JS:

JS
function MyViewModel() {
    this.price = ko.observable(25.99);
 
    this.formattedPrice = ko.computed({
        read: function () {
            return '$' + this.price().toFixed(2);
        },
        write: function (value) {
            // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
            value = parseFloat(value.replace(/[^\.\d]/g, ""));
            this.price(isNaN(value) ? 0 : value); // Write to underlying storage
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

I've also copied the code and put a jsFiddle here: http://jsfiddle.net/wDvxw/

My issue is that the value does not update itself when you enter the same thing twice. For example:

Step 1: Enter 25.1234, it becomes $25.12 Step 2: Enter 25.1234 again. Now nothing happens.

My guess is that the value did not change and thus it doesn't reformat itself. May I know how I can go about fixing this?

Thanks!

回答1:

This is an optimization in Knockout that it does not fire the changed events if you set an observable to the same value.

So if you want to always fire the change event you need to use the valueHasMutated method to manully trigger it:

this.price(isNaN(value) ? 0 : value); // Write to underlying storage
this.price.valueHasMutated();

In itself it won't fix your code because there is another optimization introduced for ko.computed in KO 3.0 so the computed are also not triggering the change if the calculated value remains the same and you need to use .extend({notify: 'always'}) to force the notification of the subscribers.

So the full working computed will look like this:

this.formattedPrice = ko.computed({
   read: function () {
      return '$' + this.price().toFixed(2);
   },
   write: function (value) {
      value = parseFloat(value.replace(/[^\.\d]/g, ""));
      this.price(isNaN(value) ? 0 : value); // Write to underlying storage
      this.price.valueHasMutated();
    },
    owner: this
}).extend({notify: 'always'});

Demo JSFiddle.