Computed Observables

如果你有监控属性firstName和lastName的话,此时如果你想要显示全名?

这个时候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的第二个参数是什么,你是否很疑惑?

在前面的代码,我们在定义computed依赖的时候用到了this,没有它,将不能够引用到this.firstName()this.lastName()。

老练的Javascript程序员就觉得很平常,但是假如不怎么了解Javascript就会觉得难以理解(如C#和Java程序员不需要设置此值,但JavaScript呢,作用域是可以被改变的)

A popular convention that simplifies things

可以用一个简单的办法去简化这种行为

这是一种比较流行的办法用于避免追踪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.

Dependency chains just work

依赖链的工作

当然,你希望你能创建一个计算监控属性链,例如,你可以这样

  • 监控属性items表述一组列表项
  • 监控属性selectedIndexes保存着被用户选上的列表项的索引
  • 依赖监控属性selectedItems 返回的是selectedIndexes 对应的列表项数组
  • 另一个依赖监控属性返回的true或false依赖于 selectedItems 的各个列表项是否包含一些属性(例如,是否新的或者还未保存的)。一些UI element(像按钮的启用/禁用)的状态取决于这个值)。
  • 然后,items或者selectedIndexes 的改变将会影响到所有依赖监控属性的链,所有绑定这些属性的UI元素都会自动更新。多么整齐与优雅!

可写的计算监控属性

初学者跳过,可写的computed observables是比较高级的了,在大多情况下都是用不到的

当你学到了,计算监控属性的值是通过计算其他监控属性得到,在感觉上计算监控属性正常情况下仅仅是只读的。

这样看起来很奇怪,那么,computed observables是否可以支持可写呢?

你只需要提供自己的回调函数做一些事情,在写值的时候。

Example 1: Decomposing user input

分解用户的输入

返回到经典的 “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 以下全部可用选项的列表

Example 2: A value converter

转化一个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函数会自动删除负号。

Example 3: Filtering and validating user input

过滤并验证用户输入

例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很难使用的话,你就可以用它。

How dependency tracking works

依赖跟踪如何工作的

初学者可以跳过,但是高级开发人员可以需要知道为什么依赖监控属性能够自动跟踪并且自动更新UI…

其实很简单而且可爱的,这个跟踪的算法是这样的:

  1. 当你声明一个依赖监控属性的时候,KO会立即调用执行函数并且获取初始化值。
  2. 当你的执行函数运行的时候,KO会把任何在监控属性(或者计算监控属性))读到的值都会都记录到一个Log列表里。
  3. 执行函数结束以后,KO会向所有Log里需要依赖到的对象进行订阅。订阅的callback函数是重新运行你的执行函数。然后回头重新执行上面的第一步操作(并且注销不再使用的订阅)。
  4. 最后KO会通知所有订阅它的订阅者,告诉它们我已经设置了新值。

所有说,KO不仅仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来说,你的依赖属性可以是动态的:依赖属性A代表你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才重新执行。你不需要再声明其它的依赖:运行时会自动探测到的。

另外一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,如果模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。

Controlling dependencies using peek

使用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.

Determining if a property is a computed observable

假如有一个属性是依赖属性

在一些场景中,如果你是处理一个依赖属性它是有用的编程方式,Knockout提供一个应用函数

ko.isComputed 将会帮助你解决这些情况

例如,数据从服务器返回回来,你可以要排除依赖属性

for (var prop in myObject) {
if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) {
result[prop] = myObject[prop];
}
}

此外,Knockout 提供了类似的功能,能够对监控属性和依赖属性起到作用

  • ko.isObservable:返回true的,监控属性,监控数组和所有的依赖属性
  • ko.isWriteableObservable:返回true, 监控属性,监控数组,和 可写的依赖属性

Computed Observable Reference

引用依赖属性

一个依赖属性可以有下面的形式构造出来:

  1. ko.computed( evaluator [, targetObject, options] ) 最常见的情况,这种形式的支持创建一个依赖属性
    • evaluator — 一个函数,用来求出依赖属性当前的值
    • targetObject — 就是回调函数中,引用当前的this,要指定作用域,详情看managing this
    • options — 为依赖属性的配置更多的属性
  2. ko.computed( 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] ) — 手工注册依赖通知

有些地方比较拗口,等看完源码后就能补准翻译了~~

Knockout 新版应用开发教程之Computed Observables的更多相关文章

  1. Knockout 新版应用开发教程之Observable与computed

    KO是什么? KO不是万能的,它的出现主要是为了方便的解决下面的问题: UI元素较多,用户交互比较频繁,需要编写大量的手工代码维护UI元素的状态.样式等属性? UI元素之间关系比较紧密,比如操作一个元 ...

  2. Knockout 新版应用开发教程之"text"绑定

    目的 DOM元素显示文本的值是你传递的参数,前提是text先绑定到该元素上 典型的常用元素 <span>或者<em>习惯性的用来显示文本,但是在技术上来说你可以用任何元素的. ...

  3. Knockout 新版应用开发教程之Observable Arrays

    假如你想到侦测和相应一个对象的改变,假如你想要侦测和响应一一组合集的改变,就要用observableArray 在许多场景都是很有用的,比如你要在UI上需要显示/编辑的一个列表数据集合,然后对集合进行 ...

  4. Knockout 新版应用开发教程之"visible"绑定

    "visible" 绑定 用途 DOM元素的显示或者隐藏是根据绑定的值来的,前提是将visible绑定给该元素 例子 <div data-bind="visible ...

  5. Xamarin Anroid开发教程之Anroid开发工具及应用介绍

    Xamarin Anroid开发教程之Anroid开发工具及应用介绍 Xamarin开发Anroid应用介绍 如今智能手机已经盛行了好几年,而针对这些智能手机的软件开发也变得异常火热.但是在Andro ...

  6. AppleWatch开发教程之Watch应用对象新增内容介绍以及编写运行代码

    AppleWatch开发教程之Watch应用对象新增内容介绍以及编写运行代码 添加Watch应用对象时新增内容介绍 Watch应用对象添加到创建的项目中后,会包含两个部分:Watch App 和 Wa ...

  7. HealthKit开发教程之HealthKit的复合数据

    HealthKit开发教程之HealthKit的复合数据 复合数据就是复合单位和值构成的数据.所谓复合单位就是由单位进行乘法.除法等得到的单位,如m/s.lb·ft等就是复合单位.本节将针对这些复合数 ...

  8. HealthKit开发教程之HealthKit的辅助数据

    HealthKit开发教程之HealthKit的辅助数据 在HealthKit中除了主要数据之外,还有6个辅助数据分别为:体积类型数据.压力类型数据.时间类型数据.温度类型数据.标量类型数据和电导率类 ...

  9. HealthKit开发教程之HealthKit的主要类型数据

    HealthKit开发教程之HealthKit的主要类型数据 在HealthKit中,我们将最常用到的数据称之为主要数据.主要数据基本上有三种:长度类型的数据.质量类型的数据.能量类型的数据.本节将主 ...

随机推荐

  1. 苹果App Store审核指南中文翻译(2014.9.1更新)

    转:http://www.cocoachina.com/appstore/20140901/9500.html CocoaChina对<苹果应用商店审核指南>中文翻译最近一次更新时间为20 ...

  2. 数据采集:完美下载淘宝Ip数据库 简单的程序节省60元人民币而不必购买数据库

    曾经做网站类型的程序时,经常需要收集客户端的访问数据,然后加以分析.这需要一个Ip数据库,数据表中显示Ip所在的省份市区等信息.网络上有流传的Ip纯真数据库,一些公开的Web服务也可以查询Ip地址信息 ...

  3. A Xamarin.Forms Infinite Scrolling ListView

    from:http://www.codenutz.com/lac09-xamarin-forms-infinite-scrolling-listview/ The last few months ha ...

  4. csdn 泄露用户密码害人不浅啊。

    先是京东被盗,接着博客园也登陆不了了.

  5. 了解 JavaScript (4)– 第一个 Web 应用程序

    在下面的例子中,我们将要构建一个 Bingo 卡片游戏,每个示例演示 JavaScript 的不同方面,通过每次的改进将会得到最终有效的 Bingo 卡片. Bingo 卡片的内容 美国 Bingo ...

  6. java中获取比毫秒更为精确的时间

    所以这里提醒做非常精确的时间统计的朋友,谨慎使用System.currentTimeMillis() . 在Java中可以通过System.currentTimeMillis()或者System.na ...

  7. [转]如果我有jQuery背景,我应该如何切换到AngularJS的思维模式?

    导言 stackoverflow上有一个人问了一个问题:如果我有jQuery背景,我应该如何切换到AngularJS的思维模式? 有一个回复非常经典,获得了两千多票. 为了让国内开发者也能领略到其中的 ...

  8. 关于ORACLE的重启命令

    有时候数据死了,或者没有监听,不必重启机子,重启一下数据库服务行了,下面是linux下的启动步骤,没有在win下执行过,可能也可以!以oracle9i为例(1) 以oracle身份登录数据库,命令:s ...

  9. 如何在IIS7下配置ASP+ACCESS环境

    如何在IIS7下配置ASP+ACCESS环境 | 浏览:901 | 更新:2013-01-16 17:46 1 2 3 4 5 6 7 分步阅读 默认装完IIS7之后,使用ASP程序会发现提示数据库连 ...

  10. Asp.net Core CacheHelper 通用缓存帮助类

    using System; using Microsoft.Extensions.Caching.Memory; using System.Runtime; namespace UFX.Tools { ...