2.可写的计算监控
初学者可能想要跳过本节 - 可写的计算监控是相当高级的部分,在大多数情况下不是必需的。
通常,计算监控是一个通过其他监控值计算出的值,因此是只读的。 令人惊讶的是,可以使计算监控值变得可写。 你只需要提供自己的回调函数,输入一些正确的值。
你可以使用一个可写的计算监控,就像一个常规的监控对象,用你自己的逻辑来定义读写。就像一个监控对象一样,您可以使用链接语法将值写入模型对象上的对过监控对象或计算监控对象。 例如,
myViewModel.fullName('Joe Smith').age(50).
可写计算监控是一个强大的功能,可能在很多场景都能会用到。
1. 例子1:分解用户输入
回到经典的名+姓=姓名
的例子,你可以把前面的例子中的fullName
计算监控变为可写,那样用户可以直接编辑全名,并且他们输入的内容将被解析并映射回到底层的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 lastSpacePos = value.lastIndexOf(" ");
if (lastSpacePos > 0) { // 忽略无内容
this.firstName(value.substring(0, lastSpacePos)); // 更新"firstName"的值
this.lastName(value.substring(lastSpacePos + 1)); // 更新"lastName"的值
}
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
这跟Hello World
示例完全相反,在这里,姓和名不可编辑,但组合的全名是可编辑的。
前面的视图模型代码演示了用于初始化计算的observable的单一参数语法。 有关可用选项的完整列表,请参阅computed observable引用。
2. 例子2:全选/全不选功能
当提供给用户一个可选择列表,通过一种方式来全选和取消全选是非常有用的。通过一个布尔值可以相当直观地表示是否所有项被选中。当该值设置为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 () {
//仅仅在主数组里面的项被添加到被选中数组中的时候快速比较一下数量
return this.selectedProduce().length === this.produce.length;
},
write: function (value) {
this.selectedProduce(value ? this.produce.slice(0) : []);
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
3. 例子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) {
//去掉不需要的字符,然后转为浮点数,把原始数据写入底层价格监控对象
value = parseFloat(value.replace(/[^.d]/g, ""));
this.price(isNaN(value) ? 0 : value); //写入底层存储
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
现在,每当用户输入新价格时,文本框将立即更新显示格式化为货币符号加两位小数的形式,无论他们输入什么格式的值。这提供了很棒的用户体验,因为用户看到软件已经将它们输入的数据理解为价格。他们知道他们不能输入多于两个小数位,因为如果他们尝试,额外的小数位将立即删除。类似地,它们不能输入负值,因为write
回调函数会除去任何减号。
4. 例子4:过滤和验证用户输入
示例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.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
的值。
注意:对于验证输入是否是数字这样简单的需求,用这种技术是浪费的。 使用jQuery Validation在元素上验证是否是数字容易得多。Knockout和jQuery Validation会一起工作得很好,如网格编辑器示例所示。但是,上述示例演示了使用自定义逻辑进行过滤和验证来控制显示相关响应消息,这是一种更通用的机制,如果您的场景比jQuery验证处理本身更复杂,这可能是有用的。