Knockout2.x开发 第十九章:扩展observables监控
2013-05-08 13:21 阅读(200)

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