监控属性、监控数组

在javascript的世界里,属性和数组是构成对象的基本元素,也是数据结构的基本元素,因此knockout针对属性和数组进行了动态的监控,也是靠着这个特性完成了MVVM的流程。

创建一个View Models

只需要声明任意的JavaScript object字面量形式或者构造函数形式均可。例如:

var myViewModel = {
    personName: 'Bob',
    personAge: 123
};

你可以为view model创建一个声明式绑定的简单view。例如:下面的代码显示personName 值:

The name is 

完成以上两部并不能完成MVVM,需要将html的内容与viewmode绑定起来,代码如下:

ko.applyBindings(myViewModel);

你可能奇怪ko.applyBindings使用的是什么样的参数,

  • 第一个参数是你想用于声明式绑定
  • 第二个参数(可选),可以声明成使用data-bind的HTML元素或者容器。例如, ko.applyBindings(myViewModel, document.getElementById('someElementId'))。它的现在是只有作为someElementId 的元素和子元素才能激活KO功能。 好处是你可以在同一个页面声明多个view model,用来区分区域。

激活绑定需要注意一下几点:

  1. 每个dom节点只能绑定一个viewmodel,该节点的内部也不能再次绑定任何viewmodel.
  2. 上述给出的例子没有第二个参数,那么这个viewmode就绑定了整个body上,这个是默认的。那么这种情况在这个页面中就只能存在一个viewmodel。
  3. 虽然页面只能存在一个viewmodel,但是viewmodel里面可以绑定组件。

监控属性(Observables)

现在已经知道如何创建一个简单的view model并且通过binding显示它的属性了。但是KO一个重要的功能是当你的view model改变的时候能自动更新你的界面。当你的view model部分改变的时候KO是如何知道的呢?答案是:你需要将你的model属性声明成observable的, 因为它是非常特殊的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监控的对象都是真实的function函数。

  • 读取监控属性(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

大多数情况下,你不需要做这些,因为内置的绑定和模板系统已经帮你做好很多事情了,可以直接使用它们。
如果要在可更改即将更改之前通知其值,则可以订阅beforeChange事件。 例如:

myViewModel.personName.subscribe(function(oldValue) {
    alert("The person's previous name is " + oldValue);
}, null, "beforeChange");

注意:Knockout不保证beforeChange和change事件成对出现,因为代码的其他部分可能会单独引发任一事件。 如果您需要跟踪observable的先前值,则由您使用订阅来捕获和跟踪它。

强制观察者总是通知订阅者

当写入包含原始值(数字,字符串,布尔值或null)的observable时,通常只有在值实际改变时才通知observable的依赖关系。 但是,可以使用内置的notify扩展器来确保observable的订阅者总是在写入时通知,即使值是相同的。 您可以将扩展器应用于可观察者,如下所示:

myViewModel.personName.extend({ notify: 'always' });

延迟和/或抑制更改通知

通常,可观察者立即通知其订户,只要它改变。 但是如果一个observable重复更改或触发昂贵的更新,您可以通过限制或延迟observable的更改通知获得更好的性能。 这是使用rateLimit扩展器实现这样,其中rateLimit为延迟的参数,单位毫秒:

// Ensure it notifies about changes no more than once per 50-millisecond period
myViewModel.personName.extend({ rateLimit: 50 });

监控数组

如果你要探测和响应一个对象的变化,你应该用observables。如果你需要探测和响应一个集合对象的变化,你应该用observableArray 。在很多场景下,它都非常有用,比如你要在UI上需要显示/编辑的一个列表数据集合,然后对集合进行添加和删除。

var myObservableArray = ko.observableArray();    // Initially an empty array
myObservableArray.push('Some value');            // Adds the value and notifies observers

关键点:监控数组跟踪的是数组里的对象,而不是这些对象自身的状态。

简单说,将一对象放在observableArray 里不会使这个对象本身的属性变化可监控的。当然你自己也可以声明这个对象的属性为observable的,但它就成了一个依赖监控对象了。一个observableArray 仅仅监控他拥有的对象,并在这些对象添加或者删除的时候发出通知。

预加载一个监控数组observableArray

如果你想让你的监控数组在开始的时候就有一些初始值,那么在声明的时候,你可以在构造器里加入这些初始对象。例如:

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);

从observableArray里读取信息

一个observableArray其实就是一个observable的监控对象,只不过他的值是一个数组(observableArray还加了很多其他特性,稍后介绍)。所以你可以像获取普通的observable的值一样,只需要调用无参函数就可以获取自身的值了。 例如,你可以像下面这样获取它的值:

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);

理论上你可以使用任何原生的JavaScript数组函数来操作这些数组,但是KO提供了更好的功能等价函数,他们非常有用是因为:

  1. 兼容所有浏览器。(例如indexOf不能在IE8和早期版本上使用,但KO自己的indexOf 可以在所有浏览器上使用)
  2. 在数组操作函数方面(例如push和splice),KO自己的方式可以自动触发依赖跟踪,并且通知所有的订阅者它的变化,然后让UI界面也相应的自动更新。
  3. 语法更方便,调用KO的push方法,只需要这样写:myObservableArray.push(...)。 比如原生数组的myObservableArray().push(...)好用多了。

下面讲解的均是observableArray的读取和写入的相关函数。

indexOf

indexOf 函数返回的是第一个等于你参数数组项的索引。例如:myObservableArray.indexOf('Blah')将返回以0为第一个索引的第一个等于Blah的数组项的索引。如果没有找到相等的,将返回-1。

slice

slice函数是observableArray相对于JavaScript 原生函数slice的等价函数(返回给定的从开始索引到结束索引之间所有的对象集合)。 调用myObservableArray.slice(...)等价于调用JavaScript原生函数(例如:myObservableArray().slice(...))。

操作observableArray

observableArray 展现的是数组对象相似的函数并通知订阅者的功能。
pop, push, shift, unshift, reverse, sort, splice
所有这些函数都是和JavaScript数组原生函数等价的,唯一不同的数组改变可以通知订阅者:

    myObservableArray.push('Some new value') 在数组末尾添加一个新项

    myObservableArray.pop() 删除数组最后一个项并返回该项

    myObservableArray.unshift('Some new value') 在数组头部添加一个项

    myObservableArray.shift() 删除数组头部第一项并返回该项

    myObservableArray.reverse() 翻转整个数组的顺序

    myObservableArray.sort() 给数组排序

默认情况下,是按照字符排序(如果是字符)或者数字排序(如果是数字)。
你可以排序传入一个排序函数进行排序,该排序函数需要接受2个参数(代表该数组里需要比较的项),如果第一个项小于第二个项,返回-1,大于则返回1,等于返回0。例如:用lastname给person排序,你可以这样写:

myObservableArray.sort (function (left, right) {
  return left.lastName == right.lastName? 0: (left.lastName < right.lastName? -1: 1)
});

myObservableArray.splice() 删除指定开始索引和指定数目的数组对象元素。
例如myObservableArray.splice(1, 3) 从索引1开始删除3个元素(第2,3,4个元素)然后将这些元素作为一个数组对象返回。
更多observableArray 函数的信息,请参考等价的JavaScript数组标准函数。

remove和removeAll

observableArray 添加了一些JavaScript数组默认没有但非常有用的函数:

  • myObservableArray.remove(someItem) 删除所有等于someItem的元素并将被删除元素作为一个数组返回
  • myObservableArray.remove(function(item) { return item.age < 18 }) 删除所有age属性小于18的元素并将被删除元素作为一个数组返回
  • myObservableArray.removeAll(['Chad', 132, undefined]) 删除所有等于'Chad', 123, or undefined的元素并将被删除元素作为一个数组返回

你可能感兴趣的:(监控属性、监控数组)