第四章:模板綁定

2018-02-24 15:25 更新

模板綁定The template binding

目的

template綁定通過(guò)模板將數(shù)據(jù)render到頁(yè)面。模板綁定對(duì)于構(gòu)建嵌套結(jié)構(gòu)的頁(yè)面非常方便。默認(rèn)情況, Knockout用的是流行的jquery.tmpl模板引擎。使用它的話,需要在安裝頁(yè)面下載和引用jquery.tmpl和jQuery框架?;蛘吣阋部梢约善渌哪0逡妫m然需要了解Knockout 內(nèi)部知識(shí)才行)。

例子

<div data-bind='template: "personTemplate"'> </div>
<script id='personTemplate' type='text/html'>
    ${ name } is ${ age } years old
    <button data-bind='click: makeOlder'>Make older</button>
</script> 

<script type='text/javascript'>
    var viewModel = {
        name: ko.observable('Bert'),
        age: ko.observable(78),
        makeOlder: function () {
            this.age(this.age() +1);
        }
    };
    ko.applyBindings(viewModel);
</script>

當(dāng)引用的observable(dependent observable)數(shù)據(jù)改變的時(shí)候,Knockout會(huì)自動(dòng)重新render模板。在這個(gè)例子里,每次點(diǎn)擊button的時(shí)候都會(huì)重新render模板。

語(yǔ)法

你可以使用任何你模板引擎支持的語(yǔ)法。jquery.tmpl執(zhí)行如下語(yǔ)法:

和observable數(shù)組一起使用{{each}}

當(dāng)然使用{{each someArray}}的時(shí)候,如果你的值是observableArray,你必須使用JavaScript類型的基礎(chǔ)數(shù)組類型{{each myObservableArray()}},而不是{{each myObservableArray}}。

參數(shù)

????主參數(shù)

??????? 語(yǔ)法快速記憶:如果你聲明的僅僅是字符串(上個(gè)例子),KO會(huì)使用模板的ID來(lái)render。應(yīng)用在模板上的數(shù)據(jù)是你的整個(gè)view model對(duì)象(例如ko.applyBindings 綁定的對(duì)象)。

??????? 更多控件,你可以傳帶有如下屬性的JavaScript對(duì)象:

????????????name(必選項(xiàng)) — 需要render的模板ID – 參考 注5 如何使用function函數(shù)聲明ID。

????????????data(可選項(xiàng)) — 需要render到模板的數(shù)據(jù)。如果你忽略整個(gè)參數(shù),KO將查找foreach參數(shù),或者是應(yīng)用整個(gè)view model對(duì)象。

????????????foreach(可選項(xiàng)) — 指定KO按照“foreach”模式render模板 – 參考 注3。

????????????afterAdd或beforeRemove(可選項(xiàng)) — 在foreach模式下使用callback函數(shù)。

????????????templateOptions(可選項(xiàng)) — 在render模板的時(shí)候,傳遞額外數(shù)據(jù)以便使用。參考 注6。

傳遞多個(gè)參數(shù)的例子:

<div data-bind='template: { name: "personTemplate", data: someObject }'> </div>

注1:Render嵌套模板

因?yàn)樵谀0謇锸褂玫氖莇ata-bind屬性來(lái)聲明的,所以嵌套模板你可以再次使用data-bind='template: ...',在上層模板的元素里。

這比模板引起的原生語(yǔ)法好用多了(例如jquery.tmpl里的{{tmpl}})。Knockout語(yǔ)法的好處在于可以在每層模板的跟著相關(guān)的依賴值,所以如果依賴改變了,KO將只會(huì)重新render依賴所在的那個(gè)模板。這將很大地改善了性能。

注2:${ val }和有何不同?

當(dāng)你在模板內(nèi)部使用data-bind屬性的時(shí)候,KO是單獨(dú)為這個(gè)綁定單獨(dú)跟蹤依賴項(xiàng)的。當(dāng)model改變的時(shí)候,KO只會(huì)更新綁定的元素以及子元素而不需要重新render整個(gè)模板。所以如果你聲明這樣的代碼是,當(dāng) someObservableValue改變的時(shí)候,KO將只是簡(jiǎn)單地更新元素的text值而不需要重新render整個(gè)模板。

不過(guò),如果模板內(nèi)部使用的observable值(例如${ someObservableValue }),如果這個(gè)observable值改變了,那KO將重新render整個(gè)模板。

這就是說(shuō),很多情況下性能要比${ someObservableValue }要好,因?yàn)橹蹈淖兊脑挷粫?huì)影響臨近元素的狀態(tài)。不過(guò)${ someObservableValue }語(yǔ)法比較簡(jiǎn)潔,如果你的模板比較小的話,還是更合適的,不會(huì)帶來(lái)大的性能問(wèn)題。

注3:使用foreach

如果需要為集合里的每一個(gè)item render一次模板,有2種方式:

??? 你可以使用模板引擎里的原生“each”語(yǔ)法,對(duì)jquery.tmpl來(lái)說(shuō)就是用{{each}}語(yǔ)法迭代數(shù)組。

??? 另外一種方式就是用Knockout的foreach模式來(lái)render。

例子:

<div data-bind='template: { name: "personTemplate",                            foreach: someObservableArrayOfPeople }'> </div>

foreach模板模式的好處是:

  • 當(dāng)往你的collection集合里添加新item項(xiàng)的時(shí)候,KO只會(huì)對(duì)這個(gè)新item進(jìn)行render模板,并且將結(jié)果附加到現(xiàn)有的DOM上。
  • 當(dāng)從collection集合里刪除item的時(shí)候,KO將不會(huì)重新render任何模板,而只是簡(jiǎn)單地刪除相關(guān)的元素。
  • KO允許通過(guò)自定義的方式聲明afterAdd和beforeRemove的callback函數(shù)添加/刪除DOM元素。然后這個(gè)callback會(huì)在刪除元素的時(shí)候進(jìn)行一些動(dòng)畫或者其它操作。

與原生的each不同之處是:在改變之后,模板引擎強(qiáng)制重新render模板里所有的內(nèi)容,因?yàn)樗揪筒魂P(guān)注KO里所謂的依賴跟蹤內(nèi)容。

關(guān)于使用foreach模式的例子,參考grid editor和animated transitions。

注4:使用afterRender選項(xiàng)

有時(shí)候,你需要在模板生成的DOM元素上深度定義邏輯。例如,你可能想再模板輸出的時(shí)候進(jìn)行截獲,然后在render的元素上允許jQuery UI命令(比如date picker,slider,或其它)。

你可以使用afterRender選項(xiàng),簡(jiǎn)單聲明一個(gè)function函數(shù)(匿名函數(shù)或者view model里的函數(shù)),在render或者重新render模板之后Knockout會(huì)重新調(diào)用它。如果你使用的是foreach,那在每個(gè)item添加到observable數(shù)組之后, Knockout會(huì)立即調(diào)用afterRender的callback函數(shù)。例如,

<div data-bind='template: { name: "personTemplate",
                            data: myData,
                            afterRender: myPostProcessingLogic }'> </div>

… 在view model里聲明一個(gè)類似的函數(shù)(例如,對(duì)象包含myData):

viewModel.myPostProcessingLogic = function (elements) {
    // "elements" is an array of DOM nodes just rendered by the template
    // You can add custom post-processing logic here
}

注5:動(dòng)態(tài)決定使用哪個(gè)模板

有時(shí)候,你可能需要根據(jù)數(shù)據(jù)的狀態(tài)來(lái)決定使用哪個(gè)模板的ID??梢酝ㄟ^(guò)function的返回ID應(yīng)用到name選擇上。如果你用的是foreach模板模式, Knockout會(huì)對(duì)每個(gè)item執(zhí)行function(將item作為參數(shù))從而將返回值作為ID,否則,該function接受的參數(shù)是整個(gè) data option或者是整個(gè)view model。

例子:

<ul data-bind='template: { name: displayMode,
                           foreach: employees }'> </ul> 
<script type='text/javascript'>
var viewModel = {
    employees: ko.observableArray([
        { name: "Kari", active: ko.observable(true) },
        { name: "Brynn", active: ko.observable(false) },
        { name: "Nora", active: ko.observable(false) }
    ]),
    displayMode: function (employee) {
        return employee.active() ?"active" : "inactive";
        // Initially "Kari" uses the "active" template, while the others use "inactive"
    }
};

// ... then later ...
viewModel.employees()[1].active(true);
// Now "Brynn" is also rendered using the "active" template.
</script>

如果你的function引用的是observable值,那當(dāng)這些值改變的時(shí)候,綁定的值會(huì)隨著改變的。這將導(dǎo)致相應(yīng)的模板重新render。

注6:使用templateOptions傳遞額外的參數(shù)

如果你在綁定模板的時(shí)候需要傳入額外的數(shù)據(jù)的話,你可以使用templateOptions對(duì)象來(lái)傳遞這些值。這可以幫助你通過(guò)一些 不屬于view model過(guò)濾條件或者字符來(lái)重用模板。另外一個(gè)好處是用在范圍控制,你可以引用通過(guò)你的模板訪問(wèn)怒道的數(shù)據(jù)。

例子,

<ul data-bind='template: { name: "personTemplate",
                           foreach: employees,
                           templateOptions: { label: "Employee:",
                                              selectedPerson: selectedEmployee } }'> </ul>

<script id='personTemplate' type='text/html'>
    <div data-bind="css: { selected: $data === $item.selectedPerson()" }">
        ${ $item.label } <input data-bind="value: name" />
    </div>
</script>

在整個(gè)例子里,personTemplate有可能都使用employee和自定義對(duì)象。通過(guò)templateOptions我們可以傳遞一個(gè)字符label和當(dāng)前已選擇項(xiàng)作為selectedPerson來(lái)控制style。在jquery.tmpl模板里,這些值可以通過(guò)訪問(wèn)$item對(duì)象的屬性得到。

注7:模板是被預(yù)編譯和緩存的

為了最大性能,Knockout內(nèi)嵌模板引擎jquery.tmpl會(huì)利用自身的功能對(duì)你的模板進(jìn)行預(yù)編譯成可執(zhí)行的JavaScript代碼,然后從編譯流程里緩存輸出。這將使模板更快更加具有可執(zhí)行性,尤其是是使用foreach循環(huán)來(lái)render相同模板的時(shí)候。

一般情況你不會(huì)注意到這個(gè),所以經(jīng)常會(huì)忘記。不過(guò),當(dāng)你在某種原因下通過(guò)編程重寫模板元素的時(shí)候并且該模板之前已經(jīng)用過(guò)一次的話,你的改變不會(huì)帶來(lái)任何render的變化,因?yàn)樵诘谝淮问褂玫臅r(shí)候已經(jīng)預(yù)編譯了并且緩存起來(lái)了。(如果這些會(huì)帶來(lái)問(wèn)題,我們將考慮在KO新版本里提供一個(gè)禁用或重設(shè)模板緩存的功能,不過(guò)好像沒(méi)有好的原因去動(dòng)態(tài)改變模板元素的內(nèi)容)。

注8:使用不同的模板引擎

如果你想使用不同的JavaScript模板引擎(或者是因?yàn)槟承┰蚰悴幌朐趈Query上使用依賴)。我們可以去為KO來(lái)寫一個(gè)不同的模板引擎,例如,在KO源代碼里的jqueryTmplTemplateEngine.js,盡管他是為了支持多個(gè)版本的jquery.tmpl而編譯。支持一個(gè)單獨(dú)的模板引擎版本相對(duì)簡(jiǎn)單多了。

依賴性

template綁定只能在引用合適的模板引擎情況下才能工作。例如提到的jquery.tmpl引擎。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)