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>