可写的计算监控(Writable computed observables)
新手可忽略此小节,可写依赖监控属性真的是太advanced了,而且大部分情况下都用不到。
一般情况下,计算监控的值是通过其他监控属性的值计算出来的,因此它是只读的。这个看似很奇怪,我们有什么办法可以让他变成可写的吗。 此时,我们只需要提供一个callback的函数就可以做到。
通过你自定义的逻辑拦所有的读写操作,你可以像使用其他监控属性一样使用计算监控属性。类似obeservables一样,你可以一次改变多个observable或者computered observable属性的值,通过以下这种方式:
myViewModel.fullName('Joe Smith').age(50)
.
可写的计算监控属性在某些场景下将会是一个非常有用的特性。
示例1:分解用户输入
返回到经典的“first name + last name = full name” 例子上,你可以让事情调回来看: 让依赖监控属性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());
这是一个Hello World 例子的反例子,姓和名都不可编辑,相反姓和名组成的姓名全称却是可编辑的。
上面的view model演示的是通过一个简单的参数来初始化计算监控属性(computed observables)。详细的参数说明请参见computed observable reference。
示例2:全选/反选所有项
当呈现给用户一系列可选择的项时,我们通常会提供全选和反选的功能。这时,可以使用一个bool值来比较直观的表示是否是全选或者是反选。当返回True时表示选择了所有项,当返回false时表示反选。
HTML:
<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>
view model:
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:value转换器
有时候你可能需要显示一些不同格式的数据,从基础的数据转化成显示格式。比如,你存储价格为float类型,但是允许用户编辑的字段需要支持货币单位和小数点。你可以用可写的计算监控属性来实现,然后解析传入的数据到基本 float类型里:
HTML:
<div>Enter bid price: <input data-bind="textInput: formattedPrice"/></div> <div>(Raw value: <span data-bind="text: price"></span>)</div>
view model:
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());
这样,当用户输入一个新的价格时,无论用户输入的是什么格式,text box就会将会将它格式化成货币符号并且只有两位小数。这样的用户体验就比较好。当用户输入了超过两位小数的数据时,系统就会自动舍弃多余的小数,只保留两位,然后用户就会知道不可以输入超过两位小数的值。同样,也不可以输入负数,因为系统会自动把负号去掉。
示例4:过滤并验证用户的输入
例1展示的是写操作过滤的功能,如果你写的值不符合条件的话将不会被写入,忽略所有不包括空格的值。
再多走一步,你可以声明一个监控属性isValid 来表示最后一次写入是否合法,然后根据真假值显示相应的提示信息。其他还有更简单的方法来做全法性验证(后面再介绍),先看下面的示例:
HTML:
<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>
View Model:
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());
现在,acceptedNumericValue 将只接受数字,其它任何输入的值都会触发显示验证信息,而会更新acceptedNumericValue。
备注:上面的例子显得杀伤力太强了,更简单的方式是在<input>上使用jQuery Validation和number class。Knockout可以和jQuery Validation一起很好的使用,参考例子:grid editor 。当然,上面的例子依然展示了一个如何使用自定义逻辑进行过滤和验证数据,如果验证很复杂而jQuery Validation很难使用的话,你就可以用它。