Writable computed observables 可写计算监控属性
可写监控属性的要求较高,并且在大多数场合下并不常用。
一般来说,计算监控属性拥有一个有其它监控属性计算得来的值,也正因如此它是通常是只读的,看起来很奇怪,但是,确实有可能让计算监控属性成为可写的,你只需要提供回调函数合理的使用可写的值做一些事情。
你可以像常规的监控属性那样使用计算监控属性,只要你有自定义的逻辑用来截获所有的读写操作。就像普通监控属性一样,你可以使用链接语法编写拥有多个监控属性或计算监控属性的对象模型,例如:
myViewModel.fullName('Joe Smith').age(50)
可写计算监控属性拥有强大的特性使得其有更广泛的使用空间。
Exameple1: Decomposing user input 解析用户输入
回到经典的例子 “first name + last name = full name”, 你可以翻过的做: 将 fullName设置成一个可写的计算监控属性, 让用户可以直接编辑 full name ,然后用户的输入值将被解析并别映射回 firstName 和 lastName 监控属性, 在这个例子中,这个 write 回调函数控制处理了输入的值并将其分解成 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 lastSpcePos = value.lastIndexOf(" ");
if (lastSpacePos > 0){
this.firstName(value.substring(0, lastSpacePos));
this.lastName(value.substring(lastSpacePos + 1));
}
},
owner: this
})
}
ko.applyBindings(new MyViewModel());
关于 Hello World 的完整例子可以参看链接。
前面的例子用代码演示了单一参数语法初始化计算监控属性的例子,参看计算监控属性可以点击链接 computed observable reference 。
Example 2: Selecting/deselecting all items 全选/全消
通常当呈现给用户一组可选的列表时,提供一个全选全消的功能非常有用,可以很直观的使用一个布尔值来表示是否选中了所有的项目,当设置 true 时选中所有项目,设置 false 时取消所有选中。
<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());
Example 3: A value converter 值的转换
有时你可能想要使用不同于数据存储的格式在屏幕上展示一个一个值,例如,你使用float存储物品价格的值,但是想要让用户使用货币符合和固定小数位数来编辑它,你可以使用可写监控属性来展示格式化的物品价格,并映射输入值到后台存储的float值上。
<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) { value = parseFloat(value.replace(/[^.d]/g, "")); this.price(isNaN(value) ? 0 : value); }, }) } ko.applyBindings(new MyViewModel());
现在,无论用户什么时候输入价格,使用什么格式输入,文本框都会立即更新显示成使用货币符号和固定两位小数格式化后的价格。这是一个很不错的用户体验,因为用户看到程序将录入的数据解释成了价格,他们就知道不能录入多余两位的金额,因为当他们尝试那样做事,多余的小数位会被立即清除,同样的,他们也不同录入负值,因为负号也会被 write 回调函数移除。
Example 4: Filtering and validating user input 过滤和验证用户输入
例1演示了计算监控属性能够有效的过滤输入的数据,不合规则的值不会被写回相关的监控属性,它将忽略不包含空格的全名。
更进一步的讨论,你可以通过一个依赖于最新的入力是否合法的 isValid 标签的开关,来决定是否在UI上提示相应的消息。有更简单的方式来做验证(下边例子用来解释),请首先考虑下边的例子演示的机制:
<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.acceptedNumbericValue = ko.observable(123);
this.lastInputWasValid = ko.observble(true);
this.attemptedValue = ko.pureComputed({
read: this.acceptedNumbericValue,
write: function (value) {
if (isNaN(value))
this.lastInputWasValid(false);
else {
this.lastInputWasValid(true);
this.acceptedNumbericValue(value);
}
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
现在, acceptedNumericValue 将只接受纯数字的值,并且任何其他的输入值都将触发验证提示而不是更新操作。
注意:类似于验证输入数字值这样的小需求,使用这个技术有点杀鸡用牛刀。使用jQuery验证器,在<input>标签上使用 number 样式类 来实现同样的功能更容易。 Knockou和jQuery的验证器能够很好的协同工作,在 grid editor 的例子中有演示。
总之,在你需要处理比jQuery原生验证器更复杂的的业务时,之前的例子便是很好的演示了使用自定义的逻辑控制用户响应的过滤验证的机制。