使用计算属性中(Computed Observable)

可写的计算属性

初学者可以跳过这一小节,可写的计算属性相对来说比较高级,在大多数情况下也是没有必要的。

正如你上面所学的,计算属性是通过计算其他监控属性而得到的一个值。从这个意义上说,计算属性通常情况下是只读的,你可能会比较惊讶,怎么可能让计算属性变的可写。你仅仅只需要提供一个回调函数来实现值的写入。

然后你可以把这个可写的计算属性当成一个普通的监控属性来使用,通过你自定义的逻辑来实现它的读和写。这个强大的功能可以拓宽我们对KO的使用范围,你可以通过链式语法在一个View Model上传入多个监控属性或者计算属性,例如:

myViewModel.fullName('Joe Smith').age(50)

示例一:分解用户输入

返回到前面经典的“first name + last name = full name” 示例,你可以在返回全名之前,使fullName计算属性变得可写,所以用户可以直接编辑全名,而程序可以将其输入的值进行解析并映射到底层绑定到firstName和lastName监控属性上。

function MyViewModel() {
    this.firstName = ko.observable('Planet');
    this.lastName = ko.observable('Earth');
    this.fullName = ko.computed({
        read: function () { },
        write: function (value) {
            varlastSpacePos = 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());

在这个例子当中,write回调事件来处理用户输入的值将其分解成“firstName”和“lastName”两个部分,并将这些值返回到底层监控属性上。你可以按照如下的方式将你的view model绑定到你的DOM对象上:

<p>Firstname:<span data-bind="text: firstName"></span></p>
<p>Lastname:<span data-bind="text: lastName"></span></p>
<h2>Hello,<input data-bind="value: fullName"/>!</h2>

这和Hello World示例是完全不同的,因为在这里“firstName”和“lastName”是不可编辑而全名确实可编辑的。前面的 view model代码只用到了一个参数进行初始化计算属性,你可以点击"computed observable reference"查看完整的可选参数选项。

示例二:值转换

有时你可能需要对底层存储的一个数据进行简单的转换之后显示给用户。例如:你可能需要存储浮点值来表示价格,但想让用户价格单位符号和固定的小数数位都一样。你可以使用一个可写的计算属性来完成价格转换,用户传入一个浮点型值自动映射成想要的格式。

return this.firstName()+" "+this.lastName();
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" servable       
            value=parseFloat(value.replace(/[^\.\d]/g,""));
            this.price(isNaN(value)?0:value);
            // Write to underlying storage
        },    owner:this });}
ko.applyBindings(new MyViewModel());   

这样就可以简单的将价格值绑定到text文本上

<p>Enter bid price:<inputdata-bind="value: formattedPrice"/></p>

现在,任何时候用户输入一个新价格,文本框中会立即更新成带有单位符号和固定小数位格式的价格数字,而无论是输入什么格式的值。这是一种很好的用户体验,因为用户能够看到软件能够很好理解他们的输入并将其转换成价格。他们知道他们不能输入两位以上的小数位,如果他们输入,额外的小数位会被立即删掉,同样,他们也不能输入负值,因为write回调方法会忽略任何的减号。

示例三:筛选和验证用户输入

示例一中展示的是写操作过滤的功能,如果你写的值不符合条件的话将不会被写入,忽略所有不包括空格的值。

更进一步,你可以声明一个监控属性isValid来表示最后一次写入是否合法,然后根据真假值显示相应的提示信息。稍后仔细介绍,先参考如下代码:

function MyViewModel() {
    this.acceptedNumericValue = ko.observable(123);
    this.lastInputWasValid = ko.observable(true);
    this.attemptedValue = ko.computed({
        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());

按照如下格式绑定DOM元素:

<p>Enter a numeric value:<inputdata-bind="value: attemptedValue"/></p>
<divdata-bind="visible: !lastInputWasValid()">That's not a number!</div>

现在,acceptedNumericValue将只接受数值,而在更新acceptedNumericValue值之前,任何其他输入的值将触发显示验证消息。

备注:上面的例子中,对于输入值进行数字验证显得有些琐碎,这样做显得得不偿失,更简单的做法是在 <input> 元素上使用Jquery验证它是否是数值类型。Knockout 和jQuery 可以很好的在一起工作,可以grid editor参考这个例子。当然,上面的例子依然展示了一个如何使用自定义逻辑进行过滤和验证数据,如果验证很复杂而jQuery Validation又无法很好实现的时候,就可以这样使用它。