监控属性(Observables)
Knockout是在下面三个核心功能是建立起来的:
- 监控属性(Observables)和依赖跟踪(Dependency tracking)
- 声明式绑定(Declarative bindings)
- 模板(Templating)
这一节,你将学到3个功能中的第一个。 在这之前, 我们来解释一下MVVM模式和view model的概念。
MVVM and View Models
Model-View-View Model (MVVM) 是一种创建用户界面的设计模式。 描述的是如何将复杂的UI用户界面分成3个部分:
-
model:你程序里存储的数据。这个数据包括对象和业务操作(例如:银子账户可以完成转账功能), 并且独立于任何UI。使用KO的时候,通常说是向服务器调用Ajax读写这个存储的模型数据。
-
view model: 在UI上,纯code描述的数据以及操作。例如,如果你实现列表编辑,你的view model应该是一个包含列表项items的对象和暴露的add/remove列表项(item)的操作方法。
注意这不是UI本身:它不包含任何按钮的概念或者显示风格。它也不是持续数据模型 – 包含用户正在使用的未保存数据。使用KO的时候,你的view models是不包含任何HTML知识的纯JavaScript 对象。保持view model抽象可以保持简单,以便你能管理更复杂的行为。
-
view: 一个可见的,交互式的,表示view model状态的UI。 从view model显示数据,发送命令到view model(例如:当用户click按钮的时候) ,任何view model状态改变的时候更新。
使用KO的时候,你的view就是你带有绑定信息的HTML文档,这些声明式的绑定管理到你的view model上。或者你可以使用模板从你的view model获取数据生成HTML。
When using KO, your view is simply your HTML document with declarative bindings to link it to the view model. Alternatively, you can use templates that generate HTML using data from your view model.
实例讲解
创建一个view model,只需要声明任意的JavaScript object。例如
var myViewModel = { personName: 'Bob', personAge: 123 };
你可以为view model创建一个声明式绑定的简单 view 。例如:下面的代码显示 personName
值:
The name is <span data-bind="text: personName"></span>
激活 Knockout
data-bind
属性尽快好用但它不是HTML的原生属性(它严格遵从HTML5语法, 虽然HTML4验证器提示有不可识别的属性但依然可用)。由于浏览器不识别它是什么意思,所以你需要激活Knockout 来让他起作用。
激活Knockout,需要添加如下的 <script>
代码块:
ko.applyBindings(myViewModel);
你可以将这个代码块放在HTML底部,或者放在jQuery的$函数或者ready 函数里,然后放在页面上面。否则knockout无法搜索到声明式绑定的dom元素。 最终生成结果就是如下的HTML代码:
The name is <span>Bob</span>
完整代码:
<body> The name is <span data-bind="text: personName"></span> </body> </html> <script type="text/javascript"> var myViewModel = { personName: 'Bob', personAge: 123 }; ko.applyBindings(myViewModel); </script>
你可能奇怪 ko.applyBindings
使用的是什么样的参数,
-
第一个参数是你想用于声明式绑定
-
第二个参数(可选),可以声明成使用data-bind的HTML元素或者容器。例如,
ko.applyBindings(myViewModel, document.getElementById('someElementId'))
。它的现在是只有作为someElementId
的元素和子元素才能激活KO功能。 好处是你可以在同一个页面声明多个view model,用来区分区域。
Observables
现在已经知道如何创建一个简单的view model并且通过binding显示它的属性了。但是KO一个重要的功能是当你的view model改变的时候能自动更新你的界面。当你的view model部分改变的时候KO是如何知道的呢?答案是:你需要将你的model属性声明成 observables的, 因为它是非常特殊的JavaScript objects,能够通知订阅者它的改变以及自动探测到相关的依赖。
例如:将上述例子的view model改成如下代码:
var myViewModel = { personName: ko.observable('Bob'), personAge: ko.observable(123) };
你根本不需要修改view – 所有的data-bind语法依然工作,不同的是他能监控到变化,当值改变时,view会自动更新。
监控属性(observables)的读和写
不是所有的浏览器都支持JavaScript的 getters and setters (比如IE),,所以为了兼容性,使用 ko.observable
监控的对象都是真实的 functions函数。
-
读取监控属性(observable)的值,只需要直接调用监控属性(observable)(不需要参数),例如
myViewModel.personName()
将返回'Bob'
,myViewModel.personAge()
将返回123
. -
写一个新值到监控属性(observable)上,调用这个observable属性并当新值作为参数。例如:调用
myViewModel.personName('Mary')
将更新name值为'Mary'
。 -
给一个model对象的多个属性写入新值,你可以使用链式语法。例如:
myViewModel.personName('Mary').personAge(50)
将会将name更新为'Mary'
并且将age更新为50
.
监控属性(observables)的特征就是监控(observed),例如其它代码可以说我需要得到对象变化的通知,所以KO内部有很多内置的绑定语法。所以如果你的代码写成 data-bind="text: personName"
, text
绑定注册到自身,一旦 personName
的值改变,它就能得到通知。
当然调用 myViewModel.personName('Mary')
改变name的值, text
绑定将自动更新这个新值到相应的DOM元素上。这就是如何将view model的改变传播到view上的。
通常情况下,你不用手工订阅,所以新手可以忽略此小节。监控属性(Observables)的显式订阅
高级用户,如果你要注册自己的订阅到监控属性(observables),你可以调用它的 subscribe
函数。例如:
myViewModel.personName.subscribe(function(newValue) { alert("The person's new name is " + newValue); });这个
subscribe
函数在内部很多地方都用到的。你也可以终止自己的订阅:首先得到你的订阅,然后调用这个对象的 dispose
函数,例如:
var subscription = myViewModel.personName.subscribe(function(newValue) { /* do stuff */ }); // ...then later... subscription.dispose(); // I no longer want notifications
依赖属性(Computed Observables)
在早期版本中叫做dependentObservable,也有人叫自动计算属性。它通常依赖于其他的Observable,通过计算得出自己的数据。当依赖项改变的时候,computed属性会接到通知,然后同步更新自身
如何使用
如果你已经有了监控属性(observable) for firstName
和 lastName
,你想显示全称怎么办?这就需要用到 依赖属性(computed observables) 了 – 这些函数是一个或多个监控属性, 如果他们的依赖对象改变,他们会自动跟着改变。
例如,下面的view model,
function AppViewModel() { var self = this; self.firstName = ko.observable('Bob'); self.lastName = ko.observable('Smith'); }
…你可以添加一个依赖属性来返回姓名全称:
function AppViewModel() { // ... leave firstName and lastName unchanged ... self.fullName = ko.computed(function() { return self.firstName() + " " + self.lastName(); }); }并且绑定到UI的元素上,例如:
The name is <span data-bind="text: fullName"></span>… 不管
firstName
还是 lastName
改变,全称fullName都会自动更新(不管谁改变,执行函数都会调用一次,不管改变成什么,他的值都会更新到UI或者其他自动计算属性上)。
完整代码:
<body> The firstName is <input data-bind="value: firstName"><p> The lastName is <input data-bind="value: lastName"><p> The name is <span data-bind="text: fullName"></span> </body> </html> <script type="text/javascript"> function AppViewModel() { var self = this; self.firstName = ko.observable('Bob'); self.lastName = ko.observable('Smith'); //下面这个属性依赖上面的2个属性存在 self.fullName = ko.computed(function(){ return self.firstName() + " " + self.lastName(); }); } ko.applyBindings(new AppViewModel()); </script>其中的<input data-bind="value: ...">也是一种绑定方式,将在下文介绍。