在我們平常的開發當中使用頻率最多的就是CRUD(添加、更新、刪除、查詢)。 而“添加”和“編輯”操作又是整個數據源的入口,在整個CRUD中占有非常重要的地位。常規情況下我們做一個編輯操作時,首先需要將實體對象從數據庫中提取出來並將其值展示在頁面上供用戶進行編輯。用戶編輯完成之后點擊提交按鈕時我們需要再將實體對象的值從頁面中提取出來,並組成一個完整的實體對象提后交到后台進行處理。
那么這種業務場景我們如何來使用knockout來完成呢?ko是一種MVVM(Model-View-ViewModel)模式的應用,本質上我們的實體對象是一個數據模型也就是model,如果我們想要它展示在View上邊那么就需要將它轉化為ViewModel。同理,如果我們想要取得頁面的數據信息,那么也就需要再將ViewModel轉換為model。這樣我們需要解決的兩個是:
1、展示數據時我們需要將model轉換為ViewModel 。
2、提交數據之前,我們需要將ViewModel轉換為model對象
Model轉換為ViewModel
ko的監控機制是一個典型的“觀查者”模式的實現。ko.observable()方法將對象轉換為一個監控對象,即:“被觀察者”,通過使用“被觀察者”的subscribe方法來注冊“觀查者”。首先,我們定義一個實體對象(entity)並將它監控起來,然后我們訂閱一個專門的用來將Model轉換為ViewModel的Listener。當這個Listener接收到entity發生變化的通知時,將對entity的屬性進行檢測,如果它沒有被監控,則將它轉換為一個監控對象。ko.isObservable是ko官方提供的一個輔助API,它返回一個Boolean值,用來判斷對象是否已經被ko監控。
//案例視圖模型 function viewModel() { var model = this; //使用ko.observable將實體對象監控起來 this.entity = ko.observable() //注冊一個用來將entity轉換為viewModel的listener this.entity.subscribe(function (newValue) { //判斷屬性children是否已經被監控 if (newValue && !ko.isObservable(newValue.children)) { //將children屬性轉為監控對象 newValue.children = ko.observableArray(newValue.children || []); //將name屬性轉換為監控對象 newValue.name = ko.observable(newValue.name); } }); }
ViewModel轉換為Model
將ViewModel轉換為Model是一個讓很多新手很痛苦的地方。常規情況下因為entity的屬性因在轉換為ViewModel時被轉換為“被觀察者”也就是由屬性變成了一個函數,我們需要它再次將它轉回為屬性。並且因為ViewModel和頁面顯示是直接相關聯的,我們這時候還不能直接修改entity對象,因為一旦發生修改,那么頁面也跟隨着發生變化。所以實際上我們需要clone一個entity對象,並將監控對象轉換為屬性。這個過程相對來講比較麻煩,也是很多人抱怨的地方。
實質上knockout已經幫我們想到並處理了這個問題。ko.toJS方法就是官方提供的一個專門用來轉換ViewModel的輔助API,它可以自動將監控對象轉為屬性對象。同時ko還提供了一個ko.toJSON方法,它會將ViewModel直接轉換為model的json字符串形式。那么我們定義一個json對象,它的值就是viewModel轉為json字符串展示在頁面上,讓我們可以即時的看到頁面發生變化時實體對象同時跟隨發生的變化。
//實體對象生成的字符串,並來在頁面上同步展示View發生的變化 this.json = ko.computed(function () { return ko.toJSON(model.entity); });
頁面ViewModel
<div id="case" data-bind="with:entity" style="padding-left:25px;"> <div> <label>名稱:</label> <input data-bind="value: name" /> </div> <div> <label>子級:</label> <ul style="display:inline-block"> <!-- ko foreach:children --> <li data-bind="text:$data"></li> <!-- /ko --> <li> <input data-bind="value:$root.newChildName" /> <input data-bind="click:$root.addChildEventHandle" type="button" value="增加" /> </li> </ul> </div> <div style="margin-top:50px;"> <label>json:</label> <label data-bind="text:$root.json"></label> </div> </div>
綁定頁面視圖
var model = new viewModel(); model.entity({ name: '張三', children: [ '狗子', '二蛋' ] }); ko.applyBindings(model, document.getElementById('case'));
