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)更新属性值,不满足时不更新。实现了拦截器的效果。