Observable Arrays 监控数组
如果你想要监测和响应某个对象的变化,你应该使用Observable监控属性;如果你想要监测和响应一个对象集合的变化,那么请使用ObservableArray监控数组,这在很多场景下都很有用,例如当你要呈现或编辑多个值时,当你需要像列表项添加或移除一样处理UI上多个重复片段时。
例如
var myObservableArry = ko.observableArray(); // Initally an empty array myObservableArray.push('Some value'); // Adds the value and notifies observers
来看看你怎样绑定监控数组到UI上并让用户能够使用它,看下边这个简单的列表的例子,在这个例子中将演示如何绑定数组
注意“Add”按钮只有在你已经有输入时才会可用,查看HTML源代码来了解怎么使用“enable”绑定
View 的代码
<form data-bind="submit: addItem">
New item:
<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
<p>Your items:</p>
<select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>
View Model 的代码
var SimpleListModel = function(items) { this.items = ko.observableArray(items); this.itemToAdd = ko.observable(""); this.addItem = function() { if (this.itemToAdd() != "") { this.items.push(this.itemToAdd()); // Adds the item. Writing to the "items" observableArray causes any associated UI to update. this.itemToAdd(""); // Clears the text box, because it's bound to the "itemToAdd" observable } }.bind(this); // Ensure that "this" is always this view model }; ko.applyBindings(new SimpleListModel(["Alpha", "Beta", "Gamma"]));
关键点: 监控数组是在跟踪监控数组中的对象,而不是这些对象的状态
单纯放入一个对象到监控数组中并不会导致对象自己本身的属性被监控。当然,如果你希望你也可以监控这些个属性,但那就是另外的监控了。一个监控数组仅仅跟踪它所持有的对象,并在这些对象被添加或移除时通知监听者。
Prepopulating and observableArray 预加载一个监控数组
如果你不希望你的监控数组一开始就是空的,而是包含一些初始对象时,你需要把这些初始对象作为一个数据传入到它的构造器中,例如
// This observable array initially contains three objects var anotherObservableArray = ko.observableArray([ { name: "Bungle", type: "Bear" }, { name: "George", type: "Hippo" }, { name: "Zippy", type: "Unknown" } ]);
Reading information from an observableArray 读取监控数组的数据
在幕后,一个监控数组实际上就是一个监控属性,而这个监控属性的值是一个数组(给监控数组额外添加了一些有用的特性)。因此,你可以像其他监控属性一样,调用监控数组的无参函数以获取它的底层数组值,然后就可以从这个数组中读取数据,例如
alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);
技术上来说,你可以使用任何原生的javascript数组函数去操作它的底层数组,但是通常情况下后边介绍的这些选择更好一点,KO的监控数组有它自己的更有用的等效函数,因为:
1. 它们可以在所有浏览器上使用,兼容性好(例如,IE8及更早版本不支持原生的javascript的 indexOf 函数, 但是KO的 indexOf 可以在任何地方使用)
2. 在操作数组的函数方面,比如 push 和 splice, KO方法的“自动触发依赖跟踪机制”可以在数据改变时让所有订阅者得到通知,并且自动更新UI元素
3. 它的语法更便捷,例如调用KO的push方法,只需要写 myObservableArry.push(...),这比调用底层数组的push写法 myObservableArray().push(...)稍微好一些
以下介绍一些监控数组的操作方法
indexOf
indexOf函数返回等于你指定参数的第一个数组项的索引。例如, myObservableArray.indexOf('Blah') 将返回第一个等于‘Blah’的数组项的基于0的索引,找不到相等的数组项时返回-1
slice
监控数组的slice函数等效于javascript原生slice函数(也就说,它返回指定开始到结束索引的数组项目),调用myObservableArray.slice(...)等效于调用底层数组的同名方法myObservableArray().slice(...)
Manipulating an 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() 对数组进行排序
默认的排序顺是字符排序(字符型)或者数字排序(数字型),你也可以传入自定义的排序函数,你需要接收两个参数(数组中的两个对象)并当第一个值较小时返回负数,第一个值较大时返回正数,相等时返回0。例如,以 last name 给一个 ‘person’ 对象的数据排序时,写法如下
myObservableArray.sort(function(left, right) { return left.lastName == right.lastName ? 0 : (left.lastName < right.lastName ? -1 : 1) })
myObservableArry.splice() 从指定的起始索引处移除指定数量的数组元素,并将移除的元素作为一个数组返回。例如,myObservableArray.splice(1,3)将从索引1开始移除3个元素(也就是说,第2,3,4个元素)并将它们以一个数组返回
查看更多监控数组函数的细节,请参照等效javascript数组的官网文档 standard javascript array functions
remove and removeAll
监控数组添加了一些很有用的javascript数组默认没有的方法
myObservableArray.remove(someItem) 移除所有等于 someItem 的元素并将它们以数组的形式返回
myObservableArray.remove(function(item) { return item.age < 18 }) 移除数组中所有age属性小于18的元素并将它们以数组的形式返回
myObservableArray.removeAll(['Chad', 132, undefined]) 移除所有等于 ‘chad’ 或 123 或 undefined 的元素并将它们以数组的形式返回
myObservableArray.removeAll() 移除数组中的所有元素
destory and destroyAll(通常只与Ruby on Rails的开发人员相关)
myObservableArray.destroy(someItem) 查找数组中所有等于 someItem 的对象元素并给它们添加叫做 _destroy的特殊属性并设置为true
myObservableArray.destroy(function(someItem){ return someItem.age < 18}) 查找数组中所有age属性小于18的对象元素并给它们添加叫做 _destroy的特殊属性并设置为true
myObservableArray.destroyAll(['Chad', 132, undefined]) 查找所有等于 ‘chad’ 或 123 或 undefined 的元素并给它们添加叫做 _destroy的特殊属性并设置为true
myObservableArray.destroyAll() 给所有元素添加叫做_destroy的特殊属性并设置为true
那么,_destroy是关于什么的呢?只有Rails的开发人员感兴趣。在Rails中它的用法如下,当你传入一个Json对象时,框架会自动帮你把它转换成ActiveRecord对象并将它保存在数据库中,框架知道哪些对象已经在数据库中存在,能够正确的使用insert或者update语句,而当你给对象添加_destory并将它设置成true时,就是告诉框架要删除这条记录。
注意,在KO渲染 foreach 绑定时,它会自动的隐藏掉任何带有_destroy属性并且值为true的对象。因此,你可以有一些类似删除按钮用来调用数组的 destory(someItem)方法,这样会让一些特殊元素立刻从你的可见UI上隐藏掉,稍后,当你向Rails提交这个json对象时,这个对象会被从数据库中删除(而其他对象将会如常插入或更新)。
Delaying and/or suppressing change notifications 通知的延迟触发
一般情况下,监控数组一旦变化,它会立刻通知它的订阅,但是如果是一个变化频繁且代价昂贵的监控数组,为了更好的性能你可以限制或者延迟监控数组的变化通知,下边的技巧使用了 rateLimit 扩展, 确保变化每50毫秒最多被通知1次:
// Ensure it notifies about changes no more than once per 50-millisencond period
myViewModel.myObservableArray.extend({rateLimit:50});