27 November 2015

正常情况下,computed observable 的值是其他 observable 对象计算得来的,computed observable 是只读的。 但是有办法让 computed observable 变量可写。你只需要在回调函数中实现一些写操作的方法就可以了。

你可以像使用正常 observable 对象那样使用一个可写的 computed observable 对象。读写规则都是自己实现的。还可以用链式写法给多个 observable 对象赋值。

可写的 computed observable 对象有强大的特征,用途广泛。

例子1

回顾经典例子 first name + last name = full name ,你可以把等式倒过来,让 fullname 可写,那么当修改了 fullname ,那么 firstname lastname 都会发生变化。

<div>First name: <span data-bind="text: firstName"></span></div>
<div>Last name: <span data-bind="text: lastName"></span></div>
<div class="heading">Hello, <input data-bind="textInput: fullName"/></div>


function MyViewModel() {
    this.firstName = ko.observable('Planet');
    this.lastName = ko.observable('Earth');
 
    this.fullName = ko.pureComputed({
        read: function () {
            return this.firstName() + " " + this.lastName();
        },
        write: function (value) {
            var lastSpacePos = value.lastIndexOf(" ");
            if (lastSpacePos > 0) { // Ignore values with no space character
                this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
                this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
            }
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

例子2 checkbox 级联

<div class="heading">
    <input type="checkbox" data-bind="checked: selectedAllProduce" title="Select all/none"/> Produce
</div>
<div data-bind="foreach: produce">
    <label>
        <input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selectedProduce"/>
        <span data-bind="text: $data"></span>
    </label>
</div>

function MyViewModel() {
    this.produce = [ 'Apple', 'Banana', 'Celery', 'Corn', 'Orange', 'Spinach' ];
    this.selectedProduce = ko.observableArray([ 'Corn', 'Orange' ]);
    this.selectedAllProduce = ko.pureComputed({
        read: function () {
            // Comparing length is quick and is accurate if only items from the
            // main array are added to the selected array.
            return this.selectedProduce().length === this.produce.length;
        },
        write: function (value) {
            this.selectedProduce(value ? this.produce.slice(0) : []);
        },
        owner: this
    });
}
ko.applyBindings(new MyViewModel());

例子3 值转化

有时候,值需要进行转化,例如钱的数额表示,需要加前缀并取两位小数。

<div>Enter bid price: <input data-bind="textInput: formattedPrice"/></div>
<div>(Raw value: <span data-bind="text: price"></span>)</div>

function MyViewModel() {
    this.price = ko.observable(25.99);
 
    this.formattedPrice = ko.pureComputed({
        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());

例子4 过滤和校验用户输入

例子1 中,我们把用户输入的值根据空格分成两部分,得到用户输入的值对应的 firstname lastname 。更深入的想下,如果用户这是后加入一些校验逻辑,那么可以做的事情就更多了。

<div>Enter a numeric value: <input data-bind="textInput: attemptedValue"/></div>
<div class="error" data-bind="visible: !lastInputWasValid()">That's not a number!</div>
<div>(Accepted value: <span data-bind="text: acceptedNumericValue"></span>)</div>

function MyViewModel() {
    this.acceptedNumericValue = ko.observable(123);
    this.lastInputWasValid = ko.observable(true);
 
    this.attemptedValue = ko.pureComputed({
        read: this.acceptedNumericValue,
        write: function (value) {
            if (isNaN(value))
                this.lastInputWasValid(false);
            else {
                this.lastInputWasValid(true);
                this.acceptedNumericValue(value); // Write to underlying storage
            }
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

注意,这个例子只是为了说明可写的 computed observable 有这种特征,但是实际上并不适合在生产中这么做。这种情况下用jquery更方便哦。