Knockout应用开发指南之模板绑定

目的

template绑定通过模板将数据render到页面。模板绑定对于构建嵌套结构的页面非常方便。默认情况, Knockout用的是流行的jquery.tmpl模板引擎。使用它的话,需要在安装页面下载和引用jquery.tmpl和jQuery框架。或者你也可以集成其它的模板引擎(虽然需要了解Knockout 内部知识才行)。

例子

 
 
 
  1.  
 
  •  
  •     ${ name } is ${ age } years old  
  •     Make older 
  •    
  •  
  •  
  •     var viewModel = {  
  •         name: ko.observable('Bert'),  
  •         age: ko.observable(78),  
  •         makeOlder: function () {  
  •             this.age(this.age() +1);  
  •         }  
  •     };  
  •     ko.applyBindings(viewModel);  
  •  
  • 当引用的observable(dependent observable)数据改变的时候,Knockout会自动重新render模板。在这个例子里,每次点击button的时候都会重新render模板。

    语法

    你可以使用任何你模板引擎支持的语法。jquery.tmpl执行如下语法:

    ◆ ${ someValue } — 参考文档

    ◆ {{html someValue}} — 参考文档

    ◆ {{if someCondition}} — 参考文档

    ◆ {{else someCondition}} — 参考文档

    ◆ {{each someArray}} — 参考文档

    和observable数组一起使用{{each}}

    当然使用{{each someArray}}的时候,如果你的值是observableArray,你必须使用JavaScript类型的基础数组类型{{each myObservableArray()}},而不是{{each myObservableArray}}。

    参数

    主参数

    语法快速记忆:如果你声明的仅仅是字符串(上个例子),KO会使用模板的ID来render。应用在模板上的数据是你的整个view model对象(例如ko.applyBindings 绑定的对象)。

    更多控件,你可以传带有如下属性的JavaScript对象:

    name(必选项) — 需要render的模板ID – 参考 注5 如何使用function函数声明ID。

    data(可选项) — 需要render到模板的数据。如果你忽略整个参数,KO将查找foreach参数,或者是应用整个view model对象。

    foreach(可选项) — 指定KO按照“foreach”模式render模板 – 参考 注3。

    afterAdd或beforeRemove(可选项) — 在foreach模式下使用callback函数。

    templateOptions(可选项) — 在render模板的时候,传递额外数据以便使用。参考 注6。

    传递多个参数的例子:

     
     
     
    1.  
     

    注1:Render嵌套模板

    因为在模板里使用的是data-bind属性来声明的,所以嵌套模板你可以再次使用data-bind='template: ...',在上层模板的元素里。

    这比模板引起的原生语法好用多了(例如jquery.tmpl里的{{tmpl}})。Knockout语法的好处在于可以在每层模板的跟着相关的依赖值,所以如果依赖改变了,KO将只会重新render依赖所在的那个模板。这将很大地改善了性能。

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

    当你在模板内部使用data-bind属性的时候,KO是单独为这个绑定单独跟踪依赖项的。当model改变的时候,KO只会更新绑定的元素以及子元素而不需要重新render整个模板。所以如果你声明这样的代码是,当 someObservableValue改变的时候,KO将只是简单地更新元素的text值而不需要重新render整个模板。

    不过,如果模板内部使用的observable值(例如${ someObservableValue }),如果这个observable值改变了,那KO将重新render整个模板。

    这就是说,很多情况下性能要比${ someObservableValue }要好,因为值改变的话不会影响临近元素的状态。不过${ someObservableValue }语法比较简洁,如果你的模板比较小的话,还是更合适的,不会带来大的性能问题。

    注3:使用foreach

    如果需要为集合里的每一个item render一次模板,有2种方式:

    你可以使用模板引擎里的原生“each”语法,对jquery.tmpl来说就是用{{each}}语法迭代数组。

    另外一种方式就是用Knockout的foreach模式来render。

    例子:

     
     
     
    1.                             foreach: someObservableArrayOfPeople }'> 
     

    foreach模板模式的好处是:

    ◆ 当往你的collection集合里添加新item项的时候,KO只会对这个新item进行render模板,并且将结果附加到现有的DOM上。

    ◆ 当从collection集合里删除item的时候,KO将不会重新render任何模板,而只是简单地删除相关的元素。

    ◆ KO允许通过自定义的方式声明afterAdd和beforeRemove的callback函数添加/删除DOM元素。然后这个callback会在删除元素的时候进行一些动画或者其它操作。

    与原生的each不同之处是:在改变之后,模板引擎强制重新render模板里所有的内容,因为它根本就不关注KO里所谓的依赖跟踪内容。

    关于使用foreach模式的例子,参考grid editor和animated transitions。

    注4:使用afterRender选项

    有时候,你需要在模板生成的DOM元素上深度定义逻辑。例如,你可能想再模板输出的时候进行截获,然后在render的元素上允许jQuery UI命令(比如date picker,slider,或其它)。

    你可以使用afterRender选项,简单声明一个function函数(匿名函数或者view model里的函数),在render或者重新render模板之后Knockout会重新调用它。如果你使用的是foreach,那在每个item添加到observable数组之后, Knockout会立即调用afterRender的callback函数。例如,

     
     
     
    1.                             data: myData,  
    2.                             afterRender: myPostProcessingLogic }'>  

    … 在view model里声明一个类似的函数(例如,对象包含myData):

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

    注5:动态决定使用哪个模板

    有时候,你可能需要根据数据的状态来决定使用哪个模板的ID。可以通过function的返回ID应用到name选择上。如果你用的是foreach模板模式, Knockout会对每个item执行function(将item作为参数)从而将返回值作为ID,否则,该function接受的参数是整个 data option或者是整个view model。

    例子:

     
     
     
    1.                            foreach: employees }'>    
    2.  
    3. var viewModel = {  
    4.     employees: ko.observableArray([  
    5.         { name: "Kari", active: ko.observable(true) },  
    6.         { name: "Brynn", active: ko.observable(false) },  
    7.         { name: "Nora", active: ko.observable(false) }  
    8.     ]),  
    9.     displayMode: function (employee) {  
    10.         return employee.active() ?"active" : "inactive";  
    11.         // Initially "Kari" uses the "active" template, while the others use "inactive"  
    12.     }  
    13. };  
    14.  
    15. // ... then later ...  
    16. viewModel.employees()[1].active(true);  
    17. // Now "Brynn" is also rendered using the "active" template.  
    18.  

    如果你的function引用的是observable值,那当这些值改变的时候,绑定的值会随着改变的。这将导致相应的模板重新render。

    注6:使用templateOptions传递额外的参数

    如果你在绑定模板的时候需要传入额外的数据的话,你可以使用templateOptions对象来传递这些值。这可以帮助你通过一些 不属于view model过滤条件或者字符来重用模板。另外一个好处是用在范围控制,你可以引用通过你的模板访问怒道的数据。

    例子,

     
     
     
    1.                            foreach: employees,  
    2.                            templateOptions: { label: "Employee:",  
    3.                                               selectedPerson: selectedEmployee } }'>  
    4.  
    5.  
    6.  
    7.      
    8.         ${ $item.label }  
    9.      
    10.  

    在整个例子里,personTemplate有可能都使用employee和自定义对象。通过templateOptions我们可以传递一个字符label和当前已选择项作为selectedPerson来控制style。在jquery.tmpl模板里,这些值可以通过访问$item对象的属性得到。

    注7:模板是被预编译和缓存的

    为了***性能,Knockout内嵌模板引擎jquery.tmpl会利用自身的功能对你的模板进行预编译成可执行的JavaScript代码,然后从编译流程里缓存输出。这将使模板更快更加具有可执行性,尤其是是使用foreach循环来render相同模板的时候。

    一般情况你不会注意到这个,所以经常会忘记。不过,当你在某种原因下通过编程重写模板