observables扩展,个人认为并没有实现新的功能点,而仅是提供了一种代码组织的手段。一般,我们可以把关于viewmodel的某个属性的操作都写入单独的扩展中,比如:数据验证(validate)、添加订阅(subscribe)等。
基本语法
ko.extenders.myExtender= function(target, option) { //do something...... return target; };创建了名为"myExtender"扩展。参数target是observable属性自身,option是用户自定义的参数。
如何使用
只需要执行extend方法,同时传入一个参数对象,对象属性名就是我们自定义的扩展名,属性值可任意,对应上面的option(即option="aaaa")。
myViewModel.firstName = ko.observable("Bob").extend({myExtender: "aaaa"});
target参数其实就是ko.observable("Bob")自身, 所以在扩展内部我们就可以使用target()读取属性值,用target("Bob123")来给属性赋值。
示例1:数据验证(validate)
验证表单数据是扩展observables的典型应用。下面举个非常简单的例子:
Html代码
<p> 输入产品名称: <input data-bind='value: productName' /> <span data-bind='visible: productName.hasError, text: productName.validationMessage'> </span> </p>Js代码
<script type="text/javascript"> 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); } //验证初始值 validate(target()); //添加订阅 target.subscribe(validate); //原样返回依赖属性,即:ko.observable(productName) return target; }; function AppViewModel(productName) { this.productName = ko.observable(productName).extend({ required: "产品名称不能为空!" }); } ko.applyBindings(new AppViewModel("HTC ONE")); </script>
一个简单的验证产品名称是否为空的例子,为空时提示"产品名称不能为空!"。在扩展内部添加了个订阅,用于验证产品是否为空。
在扩展中添加订阅是使用扩展的一种方式,另一种方式是重写computed属性。
示例2:重写computed
Html代码
<p><input data-bind="value: myNumber" /> (round to whole number)</p>Js代码
<script type="text/javascript"> ko.extenders.numeric = function(target, precision) {//precision:小数点后保留的位数 //创建一个依赖属性 var result = ko.computed({ read: target, write: function(newValue) {//重写write 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); } } }); //计算初始值的四舍五入值 result(target()); //返回创建的 computed observable return result; }; function AppViewModel(myNumber) { //this.myNumber = ko.observable(myNumber).extend({ numeric: 0 }); this.myNumber = ko.observable(myNumber).extend({ numeric: 2 }); } ko.applyBindings(new AppViewModel(221.225)); </script>对数值数字进行四舍五入的例子。在扩展内部创建了个computed属性,同时重写了write方法,最后返回这个computed属性赋值给viewModel的属性myNumber。所以执行完:this.myNumber = ko.observable(myNumber).extend({ numeric: 2 })后,属性myNumber 是个computed的。
分析实例2我们发现在扩展内部新建个computed observable(依赖监听属性),然后重写write方法可以实现对输入值的拦截。当输入值满足我们的要求时,可以调用target(valueToWrite)更新属性值,不满足时不更新。实现了拦截器的效果。