前面已經介紹了基本的綁定和模板相關知識,接下來就看ko里的數組和模板綁定,數組和模板綁定應該是實際項目中用得比較多的,ko提供了很好的支持。
一、observaleArray
前面的監控屬性都是單個對象,用的是ko.observable;有時候后台返回的是一個列表,也就是數組,這個時候就需要用監控數組了。監控數組與監控屬性幾乎一樣,只不過它是一個數組對象,擁有數組的特點。例如:創建一個簡單的監控數組:
var arr = ko.observableArray();
也可以開始就進行初始化:
arr = ko.observableArray([1,2,3]);
普通數組獲取長度就是 arr.length,而監控數組與監控屬性一樣,需要用方法,例如:arr().length。
二、監控數組常見方法
監控數組擁有普通數組的所有方法,用法也基本一樣。
2.1 push 增加一個新元素
arr.push(4);
2.2 pop 刪除一個元素,並返回這個元素
var v = arr.pop();
2.3 shift 在開始處刪除一個元素,並返回這個元素
var v = arr.shift();
2.4 unshift 在開始處增加一個元素
arr.unshift(0);
2.5 reverse 反轉數組
arr.reverse();
2.6 sort 數組排序
arr.sort();
可以看到,上面的方法和js里的數組方法一模一樣,用法也是一樣的。此外,ko還支持另外兩個方法,remove 和 removeAll:
remove:移除指定元素或移除指定條件的元素。如:arr.remove(1); 或者 arr.remove(function(item){return item > 1;});
removeAll:移除指定的元素集或移除所有元素。如:arr.removeAll([1,2]); 或者 arr.removeAll();
上面數組的內容只是簡單的數據類型,實際也可以復雜的類型。需要注意的是,為了提高性能,監控數組只監控數組對象,而不監控數組元素對象的屬性。也就是說,如果arr元素是對象,那么對arr進行的操作會反應到UI(反之也會);但對arr[0]對象內部的屬性的操作,就不會反應到UI(反之也不會)。
二、foreach 綁定
既然是數組,自然就需要遍歷輸出了。foreach 綁定就是用來遍歷集合的,如果集合是空的,會在頁面上留下一個空的模板。
2.1 foreach指定要遍歷的屬性,內部的結構會自動循環綁定。例如:
<div data-bind="foreach:list"> <p>姓名:<span data-bind="text:name"></span>,年齡:<span data-bind="text:age"></span></p> </div>
2.2 通過 as 關鍵字為遍歷的屬性定義別名,這樣就可以通過別名對它進行訪問。
<ul data-bind="foreach:{data:data,as:'data'}"> <li> <p data-bind="text:name"></p> <ul data-bind="foreach:{data:contains,as:'item'}"> <li> <p><span data-bind="text:item.name"></span>屬於<span data-bind="text:data.name"></span></p> </li> </ul> </li> </ul> var data = [ {name:"水果",contains:[{name:"蘋果",work:"蘋果的作用"},{name:"香蕉",work:"香蕉的作用"}]}, {name:"蔬菜",contains:[{name:"青菜",work:"青菜的作用"},{name:"白菜",word:"白菜的作用"}]} ]; ko.applyBindings(data);
這里要注意,as后面的名稱必須加引號。在ko里,凡是作為名稱的,都必須加引號,而如果作為對象或者屬性的,就不需要。
三、ko 模板綁定
ko 的模板用template指定,template 可以直接指定模板名稱,也支持更多選項。
3.1 直接指定模板名稱
<div data-bind="template:'tmpl'"></div>
3.2 templdate 支持更多的選項,讓我們可以更靈活的控制整個渲染過程。
name(必選項) — 需要render的模板ID。
data(可選項) — 需要render到模板的數據。如果你忽略整個參數,KO將查找foreach參數,或者是應用整個view model對象。
foreach(可選項) — 指定KO按照“foreach”模式render模板。
afterAdd或beforeRemove(可選項) — 在foreach模式下使用callback函數。
templateOptions(可選項) — 在render模板的時候,傳遞額外數據以便使用。
<div data-bind="template:{name:'koPersonList',foreach:list}"></div> <script type="text/tmpl" id="koPersonList"> <p>姓名:<span data-bind="text:name"></span>,年齡:<span data-bind="text:age"></span></p> </script>
另外,template里的name 屬性不只可以寫死為一個模板名稱,還可以是一個方法屬性,由它來動態決定使用哪個模板。例如下面的例子,在遍歷的時候,會根據名稱來決定使用哪個模板。
<div data-bind="template:{name:display,foreach:list}"></div> this.display = function(item){ if(item.name() === "tom1"){ return "tmpl1";//使用模板1渲染 } return "tmpl2";//使用模板2渲染 }
三、結合jqtmpl
集合類型通常要進行遍歷渲染,之前我們已經寫過模板引擎了,也介紹了jsRender,都支持循環遍歷。ko 默認支持的jqtmpl模板。簡單介紹一下jqtmpl,它的 tag 有:
${變量} : 輸出變量值
${{html 變量}} :輸入變量html
${{if condition}}...${{else condition}}...${{else}}...${{/if}} :if-else 判斷
${{each(index,item) 集合}...${{/each}} :遍歷
可以看出,jqtmpl和jsRender的結構是非常像。我們用template集合jqtmpl看兩個例子。
例子1:
<div data-bind="template:'person'"></div> <script type="text/tmpl" id="person"> <p>姓名:${name},年齡:${age}</p> </script>:
例子2:
<div id="" data-bind="template:{name:'personList',foreach:list}"></div> <script type="text/tmpl" id="personList"> <p>姓名:${name},年齡:${age}</p> </script> function Person(name,age){ this.name = name; this.age = age; } function ViewModel(){ this.list = [ new Person("tom",18), new Person("jack",20), new Person("lucy",22)]; } ko.applyBindings(new ViewModel());
注意:和jqtmpl一起使用時,ko.js必須先引入,后再引入jqtmpl.js,否則會沒有效果。
上面的例子就是把ko的輸出方式,換成jqtmpl的輸出方式,結果是一樣的。那么使用<span data-bind="text:someObservableValue"></span> 與 ${someObservableValue} 有什么區別呢?
當模板內部使用data-bind屬性的時候,KO是單獨為這個綁定單獨跟蹤依賴項的。當model改變的時候,KO只會更新綁定的元素以及子元素而不需要重新render整個模板。所以如果你聲明這樣的代碼是<span data-bind='text: someObservableValue'></span>,當 someObservableValue改變的時候,KO將只是簡單地更新<span>元素的text值而不需要重新render整個模板。
不過,如果模板內部使用的observable值(例如${ someObservableValue }),如果這個observable值改變了,那KO將重新render整個模板(這往往不是我們想要的)。
這就是說,很多情況下<span data-bind='text: someObservableValue'></span>性能要比${ someObservableValue }要好,因為值改變的話不會影響臨近元素的狀態。不過${ someObservableValue }語法比較簡潔,如果你的模板比較小的話,還是更合適的,不會帶來大的性能問題。
五、用ko模板完成開篇的demo
開篇我們用了多種方式完成一個簡單的demo,這樣用ko來完成相同的功能。
html:
<div id="main"> <div id="title">所有課程</div> <ul id="course" data-bind="template:{name:'courseTmpl',foreach:courseList}"></ul> </div>
模板:
<script type="text/tmpl" id="courseTmpl"> <li> <a data-bind="attr:{href:'Default.aspx?courseID='+CourseID}"> <div class="course-img"> <img data-bind="attr:{src:IconPath,alt:CourseName}"/> </div> <div class="course-info"> <div class="names"> <span data-bind="text:CourseName"></span> <span data-bind="text:TeacherName" class="fr"></span> </div> <div class="pros"> <span data-bind="text:CreatedDate"></span> <span class="fr"><span data-bind="text:StudyNumber"></span>人學習</span> </div> </div> </a> </li> </script>
js:
function CourseInfoVM(){ var self = this; this.courseList = ko.observableArray(); } var courseInfoVM = new CourseInfoVM(); ko.applyBindings(courseInfoVM,document.getElementById("course")); window.Tester.callback(function(data){ courseInfoVM.courseList(data); })
六、總結
ko的模板綁定提供了很多的功能和支持,讓我們對集合類型的處理更加方便,頁面的結構更加清晰,腳本更加簡潔。