若是你有监控属性firstName和lastName的话,此时若是你想要显示全名?
html
这个时候computed(之前叫作依赖)监控属性就出马了,这是一个函数用来依赖一个或者多个监控属性,而且当其中的任何一个依赖对象被改变的时候都将会自动更新。
程序员
例如,view model类
算法
function AppViewModel() { this.firstName = ko.observable('Bob'); this.lastName = ko.observable('Smith'); }
你能够增长一个computed计算依赖的来获得一个全名编程
function AppViewModel() { // ... leave firstName and lastName unchanged ... this.fullName = ko.computed(function() { return this.firstName() + " " + this.lastName(); }, this); }
如今你能够绑定到它的UI元素,e.g数组
The name is <span data-bind="text: fullName"></span>
当firstName或
lastName发生改变它都会更新(无论谁改变,执行函数都会调用一次,无论改变成什么,他的值都会更新到UI或者其余依赖监控属性上)
服务器
管理this
初学者跳过,你只须要安装上面例子中的代码模式写就好了,无需知道/关注这个this。
闭包
ko.computed的第二个参数是什么,你是否很疑惑?
app
在前面的代码,咱们在定义computed依赖的时候用到了this,没有它,将不可以引用到
函数this.firstName()
或 this.lastName()。
性能老练的Javascript程序员就以为很日常,可是假如不怎么了解Javascript就会以为难以理解(如C#和Java程序员不须要设置此值,但JavaScript呢,做用域是能够被改变的)
能够用一个简单的办法去简化这种行为
这是一种比较流行的办法用于避免追踪this:
若是你的模型的构造函数复制一个引用this到一个不一样的变量(一般称为self),而后你能够用self的在你的模型和没必要担忧它被从新定义指的是别的东西。
好比说
function AppViewModel() { var self = this; self.firstName = ko.observable('Bob'); self.lastName = ko.observable('Smith'); self.fullName = ko.computed(function() { return self.firstName() + " " + self.lastName(); }); }
由于self是在函数的闭包中被捕获,在任何嵌套函数仍然是同一个,例如ko.computed的evaluator,当你设计到事件句柄的时候这个技巧更有用,能够看看更多的例子live examples.
依赖链的工做
固然,你但愿你能建立一个计算监控属性链,例如,你能够这样
初学者跳过,可写的computed observables是比较高级的了,在大多状况下都是用不到的
当你学到了,计算监控属性的值是经过计算其余监控属性获得,在感受上计算监控属性正常状况下仅仅是只读的。
这样看起来很奇怪,那么,computed observables是否能够支持可写呢?
你只须要提供本身的回调函数作一些事情,在写值的时候。
分解用户的输入
返回到经典的 “first name + last name = full name” 实例,你能够把事情返过来看:
用fullName给计算监控属性写入写东西,因此你能直接编辑出全名,让用户直接输入姓名全称,而后输入的值将被解析并映射写入到基本的监控属性firstName和lastName上:
function MyViewModel() { this.firstName = ko.observable('Planet'); this.lastName = ko.observable('Earth'); this.fullName = ko.computed({ read: function () { return this.firstName() + " " + this.lastName(); }, write: function (value) { var lastSpacePos = value.lastIndexOf(" "); if (lastSpacePos > 0) { // Ignore values with no space character this.firstName(value.substring(0, lastSpacePos)); // Update "firstName" this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName" } }, owner: this }); } ko.applyBindings(new MyViewModel());
在这里例子,write的回调句柄传入了一个值被分解后传入到“firstName” 和“lastName” 上,而且写的那些值会返回给相关的监控属性
跟日常同样将这个view model绑定到DOM元素上,以下:
<p>First name: <span data-bind="text: firstName"></span></p> <p>Last name: <span data-bind="text: lastName"></span></p> <h2>Hello, <input data-bind="value: fullName"/>!</h2>
这是一个Hello World 例子的反例子,姓和名都不可编辑,相反姓和名组成的姓名全称倒是可编辑的。
以前的视图模型的代码演示单个参数的语法是为了初始化计算监控属性,看computed observable reference 如下所有可用选项的列表
转化一个value
有时候你可能须要显示一些不一样格式的数据,从基础的数据转化成显示格式。
好比,你存储价格为float类型,可是容许用户编辑的字段须要支持货币单位和小数点。
你能够用可写的依赖监控属性来实现,而后解析传入的数据到基本 float类型里:
function MyViewModel() { this.price = ko.observable(25.99); this.formattedPrice = ko.computed({ read: function () { return '$' + this.price().toFixed(2); }, write: function (value) { // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable value = parseFloat(value.replace(/[^\.\d]/g, "")); this.price(isNaN(value) ? 0 : value); // Write to underlying storage }, owner: this }); } ko.applyBindings(new MyViewModel());
而后咱们绑定formattedPrice到text box上:
<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>
因此,无论用户何时输入新价格,输入什么格式,text box里会自动更新为带有2位小数点和货币符号的数值。
这样用户能够看到你的程序有多聪明,来告诉用户只能输入2位小数,不然的话自动删除多余的位数,固然也不能输入负数,由于write的callback函数会自动删除负号。
过滤并验证用户输入
例1展现的是写操做过滤的功能,若是你写的值不符合条件的话将不会被写入,忽略全部不包括空格的值。
再多走一步,你能够声明一个监控属性isValid 来表示最后一次写入是否合法,而后根据真假值显示相应的提示信息。
稍后仔细介绍,先参考以下代码:
function MyViewModel() { this.acceptedNumericValue = ko.observable(123); this.lastInputWasValid = ko.observable(true); this.attemptedValue = ko.computed({ 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());
… 按照以下格式声明绑定元素:
<p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p> <div data-bind="visible: !lastInputWasValid()">That's not a number!</div>
如今,acceptedNumericValue 将只接受数字,其它任何输入的值都会触发显示验证信息,而会更新acceptedNumericValue。
备注:上面的例子显得杀伤力太强了,更简单的方式是在<input>上使用jQuery Validation和number class。
Knockout能够和jQuery Validation一块儿很好的使用,参考例子:grid editor 。
固然,上面的例子依然展现了一个如何使用自定义逻辑进行过滤和验证数据,若是验证很复杂而jQuery Validation很难使用的话,你就能够用它。
依赖跟踪如何工做的
初学者能够跳过,可是高级开发人员能够须要知道为何依赖监控属性可以自动跟踪而且自动更新UI…
其实很简单并且可爱的,这个跟踪的算法是这样的:
全部说,KO不只仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来讲,你的依赖属性能够是动态的:依赖属性A表明你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才从新执行。你不须要再声明其它的依赖:运行时会自动探测到的。
另一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,若是模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。
使用Peek控制依赖
Knockout’s自动跟踪依赖一般下是你想要的。可是你可能有时候须要控制某一个监控属性去更新你的计算依赖属性,特别是若是你的计算依赖可执行一些操做,
好比Ajax请求,那么peek函数就可以让你访问一个observable或者computed observable而不是建立一个依赖
在下面的例子,一个
ko.computed(function() { var params = { page: this.pageIndex(), selected: this.selectedItem.peek() }; $.getJSON('/Some/Json/Service', params, this.currentPageData); }, this);
注释:加入你不想要依赖属性太频繁的更新,你能够看 throttle extender.
假若有一个属性是依赖属性
在一些场景中,若是你是处理一个依赖属性它是有用的编程方式,Knockout提供一个应用函数
ko.isComputed 将会帮助你解决这些状况
例如,数据从服务器返回回来,你能够要排除依赖属性
for (var prop in myObject) { if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) { result[prop] = myObject[prop]; } }
此外,Knockout 提供了相似的功能,可以对监控属性和依赖属性起到做用
引用依赖属性
一个依赖属性能够有下面的形式构造出来:
evaluator — 一个函数,用来求出依赖属性当前的值
targetObject — 就是回调函数中,引用当前的this,要指定做用域,详情看managing this
options
— 为依赖属性的配置更多的属性read
— 必选,一个用来执行取得依赖监控属性当前值的函数。write — 可选,若是声明的依赖属性是可写的,那么这个函数接受一个值,那么其余代码将会试着写入到依赖属性,过自定义逻辑将值再写入各个基础的监控属性上。
owner
— 可选,若是声明,它就是KO调用read或write的callback时用到的this。deferEvaluation
— 可选,假如是true,那么依赖属性的值你不能获取,默认状况下,依赖属性获取这个值的话会马上建立disposeWhen
— 可选,待翻译,等分析源码的时候补上disposeWhenNodeIsRemoved — 可选,待翻译,等分析源码的时候补上
dispose()
— 手动配置依赖属性,清除全部订阅依赖,若是你想要中止依赖属性,当正在更新或者想要清除依赖属性的内存extend(extenders)
— 给依赖属性扩展一些内容getDependenciesCount()
— 返回当前被依赖属性依赖的数量getSubscriptionsCount()
— 返回当前依赖属性的订阅数量(或者从其余计算机的依赖属性或手动订阅)isActive()
— 返回依赖属性支持更新,若是没有依赖关系,将是无效的peek()
— 返回当前没有建立依赖关系的值(看 peek
)subscribe( callback [,callbackTarget, event] )
— 手工注册依赖通知有些地方比较拗口,等看完源码后就能补准翻译了~~