foreach binding主要做用于lists或是tables内数据单元的动态绑定。下面是一个简单的例子:javascript
<table> <thead> <tr> <th>First Name</th> <th>Last Name</th> </tr> </thead> <tbody data-bind="foreach: people"> <tr> <td data-bind="text: firstName"></td> <td data-bind="text: lastName"></td> </tr> </tbody> </table> <script type="text/javascript"> var myViewModel = { people: [ { firstName: "Chiaki", lastName: "Izumi" }, { firstName: "Kazusa", lastName: "Touma" }, { firstName: "Haruki", lastName: "Murakami" } ] }; ko.applyBindings(myViewModel); </script>
在上述示例中,咱们简单的在ko.applybindings中添加了一个数组并将其绑定在一个tbody元素中,咱们也能够自定义一个view model来实现这一效果,下面是一个更为复杂一些的例子:html
<h4>People</h4> <ul data-bind="foreach: people"> <li> Person at position <span data-bind="text: $index"></span>: <span data-bind="text: name"></span> <a href="#" data-bind="click: $parent.removePerson">Remove</a> </li> </ul> <button data-bind="click: addPerson">Add</button> <script type="text/javascript"> function MyViewModel() { var self = this; self.people = ko.observableArray([ { name: "Chiaki" }, { name: "Yuki" }, { name: "Kazusa" } ]); self.addPerson = function() { self.people.push({ name: "New name at " + new Date() }); }; self.removePerson = function() { self.people.remove(this); }; } ko.applyBindings(new MyViewModel()); </script>
参数java
data
的属性,该属性是您但愿迭代的数组。对象文字还可能具备其余属性,如afterAdd
或includeDestroyed
,有关这些额外选项的详细信息及其使用示例,请参见下面。foreach
绑定将经过在DOM中添加或删除标记的相应部分来响应数组内容的任何将来更改。如上面的示例所示,foreach块中的绑定能够引用数组条目上的属性。 例如,示例1引用了每一个数组条目的firstName和lastName属性。
可是,若是您想引用数组条目自己(而不只仅是它的一个属性),该怎么办?在这种状况下,可使用特殊的上下文属性$data
。在foreach块中,它表示当前项。例如:node
<ul data-bind="foreach: months"> <li> The current item is: <b data-bind="text: $data"></b> </li> </ul> <script type="text/javascript"> ko.applyBindings({ months: [ 'Jan', 'Feb', 'Mar', 'etc' ] }); </script>
若是须要,能够在引用每一个条目上的属性时使用$data做为前缀。例如,您能够将示例1的部分重写以下jquery
<!-- $data 引用对象每一个值--> <td data-bind="text: $data.firstName"></td>
但您没必要这样作,由于firstName在默认状况下将在$data
上下文中进行计算。若是数组中的项是被监控的,$data
将引用每一个监控的值。要引用可观察对象自己,推荐使用$rawData
。git
<!--$rawDat 引用对象自己--> <td data-bind="text: $rawData.firstName"></td>
从上面的示例2能够看出,可使用$index
引用当前数组项的从零开始的索引。$index
是一个可观察的对象,当项目的索引起生变化时(例如,若是项目被添加到数组中或从数组中删除),$index
就会被更新。
相似地,您可使用$parent
引用来自foreach
外部的数据,例如。github
<h1 data-bind="text: blogPostTitle"></h1> <ul data-bind="foreach: likes"> <li> <b data-bind="text: name"></b> likes the blog post <b data-bind="text: $parent.blogPostTitle"></b> </li> </ul> <script type="text/javascript"> function AppViewModel() { var self = this; self.blogPostTitle =ko.observable('你好'); self.likes = ko.observableArray([ { name: 'Bert' }, { name: 'Charles' }, { name: 'Denise' } ]); } ko.applyBindings(new AppViewModel()); </script>
在注释1中提到,咱们可以经过$data来调用foreach绑定的数组自己,可是当咱们使用嵌套的foreach,内层foreach如何可以调用外层foreach绑定的数组呢?这时咱们能够借由as给外层foreach所绑定的数组定义一个另外的名称,进而在内层foreach中利用此名称来调用外层foreach所绑定的数组。接下来是一个简单的例子:ajax
<ul data-bind="foreach: { data: person, as: 'person' }"> <li> <ul data-bind="foreach: { data: friends, as: 'friends' }"> <li> <span data-bind="text: person.name"></span>: <span data-bind="text: friends"></span> </li> </ul> </li> </ul> <script> var viewModel = { person: ko.observableArray([ { name: "Chiaki", friends: ["Charlie", "Kazusa"] }, { name: "Kazusa", friends: ["Chiaki", "Charlie"] }, { name: "Charlie", friends: ["Chiaki", "Kazusa"] } ]) }; ko.applyBindings(viewModel); </script>
这个例子中的外层foreach绑定的是person数组,person数组中有一个属性name和另一个数组firends,在内层foreach中绑定的是数组firends。当咱们在内层foreach要调用外层的person数组内的属性时,借由as,使用了person.name来调用。而这里也有一个有趣的状况,就是当一个数组里面只有单纯的元素,好比说friends数组,它的元素并非object,也就不存在这identifier的问题,这时咱们要调用它的元素时,只须要调用数组自己便可,这也就是为何在以前的示例中若是咱们调用绑定的数组自己会返回[object, object]。api
这代表,通常状况下,遍历数组中的元素只须要调用数组名(as指定)或是调用$data便可,而碰到那些内部元素是object的时候,咱们要调用object内的属性则须要调用相应属性的名称。数组
另外须要注意的一点就是,as后所跟着的必须是一个字符串(as: "person"而不是as: person)。
有些状况下,咱们使用foreach的场合会比较复杂,好比说以下的例子:
<ul> <li>Header item</li> <!-- The following are generated dynamically from an array --> <li>Item A</li> <li>Item B</li> <li>Item C</li> </ul>
这种状况下,咱们并不知道改在哪一个元素内添加foreach。若是是在ul添加的话,咱们事先写好的header item便会被覆盖掉,而ul元素内又只能嵌套li元素,添加另外一个容器来实现foreach也是不可行的。为了解决这一问题,咱们须要使用无容器的控制流语法(containerless control flow syntax),与先前所提到的containerless text syntax相似。一个简单的例子以下:
<!-- 不使用foreach,使用容器 --> <ul> <li>Header item</li> <!-- ko foreach: people --> <li>name: <span data-bind="text: $data"></span></li> <!-- /ko --> </ul> <!-- 使用foreach --> <ul data-bind="foreach: people"> <li>Header item</li> <li>name: <span data-bind="text: $data"></span></li> </ul> <script> var viewModel = { people: ko.observableArray(["Kazusa", "Chiaki", "Yuki"]) }; ko.applyBindings(viewModel); </script>
当咱们须要在生成的DOM元素上运行一些自定义的逻辑时,咱们能够用到
须要注意的是,这些回调函数仅仅适用于触发与列表的改变有关的动画,若是咱们须要对新添加的DOM元素附加一些其余的行为,好比说事件的处理或是第三方的UI控制,将这些行为做为自定义的绑定(custom binding)会更为合适,由于这样设定的行为是与foreach互相独立的。
接下来是一个调用afterAdd的简单的例子,其中用到了jQuery Color plugin。
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js"></script> <script src="http://code.jquery.com/color/jquery.color-2.1.2.min.js"></script> <script src="../js/knockout-3.5.0rc2.debug.js"></script>
<ul data-bind="foreach: { data: people, afterAdd: yellowFadeIn }"> <li data-bind="text: $data"></li> </ul> <button data-bind="click: addItem">Add</button> <script> var viewModel = { people: ko.observableArray(["Kazusa", "Chiaki", "Yuki"]), yellowFadeIn: function(element, index, data) { $(element) .filter("li") .animate({ backgroundColor: "yellow" }, 200) .animate({ backgroundColor: "white" }, 800); }, addItem: function() { this.people.push("New person"); } }; ko.applyBindings(viewModel); </script>
如下是对一些回调函数详尽的介绍:
afterRender
是在foreach模块初始化或是添加新的元素时触发的,其接受的参数为(为了可以保持愿意,这里用英文显示,下同):
An array of the inserted DOM elements
The data item against which they are being bound
afterAdd
与afterRender
相似,不过它只会在新元素添加时触发(foreach初始化的时候并不会触发),它所接受的参数为:
A DOM node being added to the document
The index of the added array element
The added array element
beforeRemove
函数会在数组中的某一项被删除时触发。须要注意的是,beforeRemove实际上替代了UI界面对remove所作出的回应,即在beforeRemove函数中若是不对DOM相应的元素进行remove操做,则在页面上的元素是不会被删除的,可是viewModel中的数组相应的元素却已经被删除了。beforeRemove函数接受如下参数:
A DOM node that you should remove
The index of the removed array element
The removed array element
beforeMove
函数会在数组中的元素索引起生变化时触发,beforeMove会应用于全部索引产生变化的元素,即倘若咱们在数组开头插入元素,则其后全部元素都将受到beforeMove回调函数的影响。一个比较经常使用的作法是经过beforeMove来保存原有元素的坐标以便咱们可以在afterMove中控制元素移动的动画。beforeMove函数接受如下参数:
A DOM node that may be about to move
The index of the moved array element
The moved array element
afterMove
函数也会在数组中的元素索引起生变化时触发,afterMove会应用于全部索引产生变化的元素,即倘若咱们在数组开头插入元素,则其后全部元素都将受到afterMove回调函数的影响。afterMove函数接收如下参数:
A DOM node that may have moved
The index of the moved array element
The moved array element
对于回调函数中的before
和after
,咱们应该有一个比较清醒的认识。before和after针对的都是UI界面中的元素变化,也就是页面产生变化以前和页面产生变化以后,与此同时,viewModel部分的数组已经发生了变化,正是viewModel部分的数组的变化才触发了before和after所对应的回调函数。
if binding与visible binding相似。不一样之处在于,包含visible binding的元素会在DOM中一直保存,而且该元素相应的data-bind属性会一直保持,visible binding只是利用CSS来触发元素的可见性。另外一方面,if binding是物理地增长或删除包含它的元素,而且元素内的data-bind只有在判断语句为真时才生效。
下面是一个简单的if binding的例子:
<label> <input type="checkbox" data-bind="checked: displayMessage" /> Display message </label> <div data-bind="if: displayMessage">Here is a message. Astonishing.</div> <script type="text/javascript"> ko.applyBindings({ displayMessage: ko.observable(false) }); </script>
在下面的例子中,<div>
元素对于“Mercury”是空的,可是对于“Earth”是填充的。这是由于 Earth 有一个非空 capital
属性,而 Mercury 的 capital 属性为 null
。
<ul data-bind="foreach: planets"> <li> Planet: <b data-bind="text: name"> </b> <div data-bind="if: capital"> Capital: <b data-bind="text: capital.cityName"> </b> </div> </li> </ul> <script> ko.applyBindings({ planets: [ { name: 'Mercury', capital: null }, { name: 'Earth', capital: { cityName: 'Barnsley' } } ] }); </script>
重要的是要理解 if
绑定对于使代码正常工做很是重要。没有它,在评估时就会出现错误。“Mercury”上下文中的 capital.cityName
,其中 capital
为 null
。在JavaScript中,不容许计算空值或未定义值的子属性。
if binding也支持无容器的控制流语法,一个简单的示例以下:
<label> <input type="checkbox" data-bind="checked: displayMessage" /> Display message </label> <ul> <li>Item 1</li> <!-- ko if: displayMessage --> <li>Message</li> <!-- /ko --> </ul> <div data-bind="if: displayMessage">Here is a message. Astonishing.</div> <script type="text/javascript"> ko.applyBindings({ displayMessage: ko.observable(false) }); </script>
<div data-bind="ifnot: someProperty">...</div>
等价于
<div data-bind="if: !someProperty()">...</div>
with
和 using
绑定建立一个新的绑定上下文,以便将后代元素绑定到指定对象的上下文中。(这些绑定之间的区别将在下面的参数中描述。)
固然,您能够任意嵌套使用with
绑定以using
及其余控制流绑定(如if
和foreach
)。
下面是一个将绑定上下文切换到子对象的很是基本的示例。注意,在 data-bind
属性中,没有必要在经纬度前面加上coords
。,由于绑定上下文已切换到coords
。
这里也可使用 with
。
<h1 data-bind="text: city"> </h1> <p data-bind="using: coords"> Latitude: <span data-bind="text: latitude"> </span>, Longitude: <span data-bind="text: longitude"> </span> </p> <script type="text/javascript"> ko.applyBindings({ city: "London", coords: { latitude: 51.5001524, longitude: -0.1262362 } }); </script>
<form data-bind="submit: getTweets"> Twitter account: <input data-bind="value: twitterName" /> <button type="submit">Get tweets</button> </form> <div data-bind="with: resultData"> <h3> Recent tweets fetched at <span data-bind="text: retrievalDate"> </span> </h3> <ol data-bind="foreach: topTweets"> <li data-bind="text: text"></li> </ol> <button data-bind="click: $parent.clearResults">Clear tweets</button> </div> <script type="text/javascript"> function AppViewModel() { var self = this; self.twitterName = ko.observable("@example"); self.resultData = ko.observable(); // No initial value self.getTweets = function() { var name = self.twitterName(), simulatedResults = [ { text: name + " What a nice day." }, { text: name + " Building some cool apps." }, { text: name + " Just saw a famous celebrity eating lard. Yum." } ]; self.resultData({ retrievalDate: new Date(), topTweets: simulatedResults }); }; self.clearResults = function() { self.resultData(undefined); }; } ko.applyBindings(new AppViewModel()); </script>
从这里例子中,咱们能够看出with binding在使用时的几个特色。
$parent
或root
,这部分能够参考The binding context。假若绑定的binding context是一个observable,包含with binding的元素会随着observable的变化而移除现有的子孙元素并添加一系列隶属于新的binding context的子孙元素。
相似的,with binding也提供无容器的控制流语法,这里省略例子,能够参考if binding等。
若是您提供的表达式包含任何监控的值,则每当这些值发生更改时,将从新计算该表达式。这些绑定在绑定值发生变化时的反应不一样:
- 对于with绑定,将清除后代元素,并将标记的新副本添加到文档中,并在新值的上下文中绑定。
- 对于using绑定,后代元素将保留在文档中,并使用新的上下文值从新评估它们的绑定。
$data
上下文变量引用对象,可是使用as选项为它提供一个更具描述性的名称可能会颇有用<div data-bind="with: currentPerson, as: 'person'"></div>
as
选项的默认行为是为提供的对象设置一个名称,同时仍然将内容绑定到对象。可是您可能更愿意保持上下文不变,只设置对象的名称。后一种行为多是未来版本的击倒的默认行为。若要打开特定绑定,请将noChildContext
选项设置为true
。当这个选项与as
一块儿使用时,对对象的全部访问都必须经过给定的名称,而且$data
将保持设置为外部viewmodel。using: currentPerson, as: 'person', noChildContext: true
,你可使用 let: { person: currentPerson }
。与其余控制流绑定(如if和foreach)同样,可使用with和using而不使用任何容器元素来承载它。若是您须要在不合法的地方使用这些绑定,仅仅为了保存绑定而引入新的容器元素,那么这是很是有用的。
<ul> <li>Header element</li> <!-- ko with: outboundFlight --> ... <!-- /ko --> <!-- ko with: inboundFlight --> ... <!-- /ko --> </ul>
当不须要从新呈现后代元素时,他在knockoutjs 3.5中using
了绑定来替代with
。(参考with和using区别) 由于using
从新评估后代绑定而不是从新呈现,每一个后代绑定将包含对使用上下文的附加依赖。
component 组件绑定将指定的组件注入元素,并可选地向其传递参数。
<h4>First instance, without parameters</h4> <div data-bind='component: "message-editor"'></div> <h4>Second instance, passing parameters</h4> <div data-bind='component: { name: "message-editor", params: { initialText: "Hello, world!" } }'></div> <script type="text/javascript"> ko.components.register('message-editor', { viewModel: function (params) { this.text = ko.observable(params && params.initialText || ''); }, template: 'Message: <input data-bind="value: text" /> ' + '(length: <span data-bind="text: text().length"></span>)' }); ko.applyBindings(); </script>
注意:在更现实的状况下,您一般会从外部文件加载组件视图模型和模板,而不是将它们硬编码到注册中。请参见示例和注册文档。
有两种方法可使用组件绑定
<div data-bind='component: "my-component"'></div>
简写值也能够被监控到。在本例中,若是组件绑定发生更改,则组件绑定将处理(dispose)旧组件实例,并注入新引用的组件。例子
<div data-bind='component: observableWhoseValueIsAComponentName'></div>
name
要注入的组件的名称。这也是能够观察到的。params
将传递给组件的对象。一般,这是一个包含多个参数的键值对象,一般由组件的viewmodel构造函数接收。<div data-bind='component: { name: "shopping-cart", params: { mode: "detailed-list", items: productsList } }'></div>请注意,不管什么时候删除组件(要么是由于名称更改了observable,要么是由于封闭的控制流绑定删除了整个元素),删除的组件被释放( disposed)。
1.组件加载器被要求提供 viewmodel 工厂和模板
一般,这是一个异步过程。 它可能涉及对服务器的请求。 对于API一致性,默认状况下Knockout确保加载过程做为异步回调完成,即便组件已经加载并缓存在内存中也是如此。 有关此内容以及如何容许同步加载的更多信息,请参阅控制同步/异步加载。
2.组件模板被克隆并注入容器元素
3.若是组件有一个视图模型 viewmodel ,则实例化它
4.视图模型 viewmodel 绑定到视图
5.组件被激活
6.组件被拆除,视图模型被放置
组件一般有视图模型,但不必定非得有。组件能够只指定模板。
在本例中,绑定组件视图的对象是传递给组件绑定的params对象。例子
ko.components.register('special-offer', { template: '<div class="offer-box" data-bind="text: productName"></div>' });
能够注入:
<div data-bind='component: { name: "special-offer-callout", params: { productName: someProduct.name } }'></div>
或者,更方便地,做为自定义元素
<special-offer params='productName: someProduct.name'></special-offer>
<!-- ko component: { name: "message-editor", params: { initialText: "Hello, world!", otherParam: 123 } } --> <!-- /ko -->
附加组件绑定的元素可能包含进一步的标记。例如
<div data-bind="component: { name: 'my-special-list', params: { items: someArrayOfPeople } }"> <!-- Look, here's some arbitrary markup. By default it gets stripped out and is replaced by the component output. --> The person <em data-bind="text: name"></em> is <em data-bind="text: age"></em> years old. </div>
尽管该元素中的DOM节点将被删除,而且默认状况下不进行绑定,但它们不会丢失。相反,它们被提供给组件(在本例中是my-special-list
),组件能够按照本身的意愿将它们包含在输出中。
若是您想要构建表示容器UI元素的组件,例如网格、列表、对话框或选项卡集,这是很是有用的,由于这些元素须要将任意标记注入并绑定到公共结构中。有关自定义元素的完整示例,它也能够在没有使用上面所示语法的自定义元素的状况下工做。
<!-- This could be in a separate file --> <template id="my-special-list-template"> <h3>Here is a special list</h3> <ul data-bind="foreach: { data: myItems, as: 'myItem' }"> <li> <h4>Here is another one of my special items</h4> <!-- ko template: { nodes: $componentTemplateNodes, data: myItem } --><!-- /ko --> </li> </ul> </template> <my-special-list params="items: someArrayOfPeople"> <!-- Look, I'm putting markup inside a custom element --> The person <em data-bind="text: name"></em> is <em data-bind="text: age"></em> years old. </my-special-list> <script type="text/javascript"> ko.components.register("my-special-list", { template: { element: "my-special-list-template" }, viewModel: function(params) { this.myItems = params.items; } }); ko.applyBindings({ someArrayOfPeople: ko.observableArray([ { name: "Lewis", age: 56 }, { name: "Hathaway", age: 34 } ]) }); </script>