翻译:使用 Knockout 扩展器扩展 observables
原文地址:http://knockoutjs.com/documentation/extenders.html
原文名称:Using extenders to augment observables
在值发生变化的时候,Knockout 的可观察对象提供了基本的功能来支持读/写,以及通知订阅者。在有些情况下,你可能希望能为可观察对象添加一些功能. 包括增加一些属性,或者通过为可观察对象增加写入的附加处理, Knockout 扩展器提供了一种简单并且灵活的途径,支持实现参数化的可观察对象。
如何创建一个扩展器
创建扩展器涉及到为 ko.extenders 对象添加一个函数,在这个函数被调用的时候,可观察对象将作为第一个参数,其它的选项被作为第二个参数。函数既可以返回可观察对象本身,也可以返回通过某种方式创建的新的可观察对象。
下面是一个简单的例子,logChange 扩展订阅了主题对象,提供了一个可配置的提示信息,在可观察对象发生变化的时候,在控制台输出这个提示信息和可观察对象的最新值。
ko.extenders.logChange = function(target, option) { target.subscribe(function(newValue) { console.log(option + ": " + newValue); }); return target; };
通过在可观察对象上调用 extend 函数可以使用这个扩展,extend 的参数是一个对象,其中名为 logChange 的属性表示使用这个扩展,属性的值就是扩展函数的第二个参数的值。
this.firstName = ko.observable("Bob").extend({logChange: "first name"});
这样,如果 firstName
可观察对象的值修改为 Ted ,就会在控制台输出 first name: Ted
.
示例1:强制输入数字
这个示例创建一个扩展强制对可观察对象只能提供带有指定小数位数的数字。在这里,扩展器创建了一个新的允许写的支持计算的可观察对象,注入在真正的可观察对象之前进行写入处理。
源代码如下所示:
<p><input data-bind="value: myNumberOne" /> (round to whole number)</p> <p><input data-bind="value: myNumberTwo" /> (round to two decimals)</p>
扩展的源代码和模型如下:
ko.extenders.numeric = function(target, precision) { //create a writeable computed observable to intercept writes to our observable var result = ko.computed({ read: target, // 返回源对象的值 write: function(newValue) { var current = target(), roundingMultiplier = Math.pow(10, precision), newValueAsNum = isNaN(newValue) ? 0 : parseFloat(+newValue), valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier; // 如果发生变化了,写入源对象 if (valueToWrite !== current) { target(valueToWrite); } else { //if the rounded value is the same, but a different value was written, force a notification for the current field if (newValue !== current) { target.notifySubscribers(valueToWrite); } } } }); //initialize with current value to make sure it is rounded appropriately result(target()); //return the new computed observable return result; }; function AppViewModel(one, two) { this.myNumberOne = ko.observable(one).extend({ numeric: 0 }); this.myNumberTwo = ko.observable(two).extend({ numeric: 2 }); } ko.applyBindings(new AppViewModel(221.2234, 123.4525));
示例中2:为可观察对象添加验证
这个示例为可观察对象添加一个必须提供的验证扩展。不会返回新对象,这个扩展为可扩展对象添加了一个子可观察对象。由于可观察对象实际上是一个函数,可以拥有真正的属性。然而,当视图模型转换为 JSON 的时候,子可观察对象将会被丢弃掉,而留下真正的可观察对象的值。对于仅仅与 UI 相关而不需要发送回服务器的情况,是一个不错的添加功能的方式。
视图代码如下:
<p data-bind="css: { error: firstName.hasError }"> <input data-bind='value: firstName, valueUpdate: "afterkeydown"' /> <span data-bind='visible: firstName.hasError, text: firstName.validationMessage'> </span> </p> <p data-bind="css: { error: lastName.hasError }"> <input data-bind='value: lastName, valueUpdate: "afterkeydown"' /> <span data-bind='visible: lastName.hasError, text: lastName.validationMessage'> </span> </p>
扩展和视图模型的实现。
ko.extenders.required = function(target, overrideMessage) { // 添加子可观察对象 target.hasError = ko.observable(); target.validationMessage = ko.observable(); // 执行验证的函数 function validate(newValue) { target.hasError(newValue ? false : true); target.validationMessage(newValue ? "" : overrideMessage || "This field is required"); } // 初始验证 validate(target()); // 当值发生变化的时候进行验证 target.subscribe(validate); // 返回源可观察对象 return target; }; function AppViewModel(first, last) { this.firstName = ko.observable(first).extend({ required: "Please enter a first name" }); this.lastName = ko.observable(last).extend({ required: "" }); } ko.applyBindings(new AppViewModel("Bob","Smith"));
应用多个扩展
也可以对可观察对象应用多个扩展。
this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" });
在这个例子中,可观察对象应用了两个扩展。