昨天我們通過Knockoutjs環境搭建一文對Knockoutjs進行了簡單的了解,我們在使用Js的很多情況下都是為了實現頁面的局部刷新從而可以獲得數據,使用Knockoutjs的情況也不例外,在Knockoutjs中提供了屬性監控(Observables)和依賴跟蹤(Dependency tracking)這兩個概念,通過它們可以對我們所關心的控件(比如:text文本框)實現動態監控,這樣,當空間的值改變或者出現對應的事件是,Knockoutjs可以對其最初相應的反應(此反應不需要刷新頁面),從而得到我們想要的結果。
下面我們來看一個比較經典的例子:一個人的名字由“姓”和“名”組成,由用戶輸入一個姓名,中間由空格隔開,然后通過span動態顯示用戶的姓和名。代碼如下:
1 <script type="text/javascript" src="knockout-2.2.0.js"></script> 2 3 <p>First name: <span data-bind="text: firstName"></span></p> 4 <p>Last name: <span data-bind="text: lastName"></span></p> 5 <h2>Hello, <input data-bind="value: fullName"/>!</h2> 6 7 <script type="text/javascript"> 8 function MyViewModel() { 9 this.firstName = ko.observable('張'); 10 this.lastName = ko.observable('三'); 11 12 this.fullName = ko.computed({ 13 read: function () { 14 return this.firstName() + " " + this.lastName(); 15 }, 16 write: function (value) { 17 var lastSpacePos = value.lastIndexOf(" "); 18 if (lastSpacePos > 0) { 19 this.firstName(value.substring(0, lastSpacePos)); 20 this.lastName(value.substring(lastSpacePos + 1)); 21 } 22 }, 23 owner: this 24 }); 25 } 26 27 ko.applyBindings(new MyViewModel()); 28 </script>
在講解以上的代碼之前,我們首先來了解一下MVVM(Model-View-View Model)的概念
一、MVVM(Model-View-View Model)
MVVM是一種用戶界面的設計模式,它將精巧、復雜的設計算法隱藏在用戶界面的背后,從而使我們可以更加方便的使用它。它分為以下的三個部分:
(1)、Model層:此類似於MVC中的Model層,用來保存我們應用程序的數據。在使用Knockoutjs時,我們通常是使用Ajax來向服務端付出請求來讀寫Model層的數據的。
(2)、View Model層:此層是建立在UI上面的純粹的數據操作。它是和我們的HTML代碼分離開來的JavaScript對象,但並不代表持久化對象,它只是對用戶正在操作的且沒有進行保存的數據,此設計可以保持View Model的純潔性,從而能更好的處理復雜的數據操作。
(3)、View層:此層是用戶可以看到的,它用來顯示從View Model層傳遞過來的數據、發出請求命令(比如用戶點擊了某個Button)、當View Model的值改變時作出相應的更新等。在使用Knockoutjs時,此層僅僅代表HTML頁面或者是服務端通過模版生成的HTML頁面。
在了解完MVVM的概念后,我們就可以着手分析以上的代碼了:
二、首先定義ViewModel
function MyViewModel(){}
在此ViewModel中又定義了三個屬性:
1 this.firstName = ko.observable('張'); 2 this.lastName = ko.observable('三'); 3 4 this.fullName = ko.computed({});
我們先來看前兩個屬性,即:firstName和lastName,這兩個屬性都是使用ko.observable()來定義的,使用此方法來定義就是告訴Knockoutjs屬性firstName和lastName需要進行屬性監控,這樣當firstName和lastName的值改變時,就會觸動對應的View層的組件做出相應的改變,這樣才會實現當用戶輸入完fullName之后,firstName和lastName做出相應的改變。
三、Computed Observables(組合監控屬性)
我們在定義第三個fullName屬性時使用的是:ko.computed()方法,此方法代表將多個監控屬性組合到一起(在這里我們將firstName和lastName進行組合形成了fullName),當組合屬性中的任何一個監控屬性的值改變時,組合屬性的值也會做出相應的改變。
1 this.fullName = ko.computed({ 2 read: function () { 3 return this.firstName() + " " + this.lastName(); 4 }, 5 write: function (value) { 6 var lastSpacePos = value.lastIndexOf(" "); 7 if (lastSpacePos > 0) { 8 this.firstName(value.substring(0, lastSpacePos)); 9 this.lastName(value.substring(lastSpacePos + 1)); 10 } 11 }, 12 owner: this 13 });
在此例中,我們使用了組合屬性的三個參數:read、write和owner。其中,
(1)、read是必須的,表示返回此組合屬性的值。
(2)、write是可選的,如果我們使用此屬性,則代表此組合屬性是可寫的,當從View層傳入一個value值,則我們可以根據自己的需要對值進行處理。這里我需要將value根據空格拆分,然后分別賦值給firstName和lastName。
(3)、owner可選,一般值為this。
四、激活屬性監控
1 ko.applyBindings(new MyViewModel());
這里我們使用ko.applyBindings()對我們定義的MyViewModel進行激活,這樣我們在進行綁定時就可以看到各個屬性的值,如果沒有進行激活的話,則不會顯示對應屬性的值,這點我們要注意了。
五、綁定屬性到View層
1 <p>First name: <span data-bind="text: firstName"></span></p> 2 <p>Last name: <span data-bind="text: lastName"></span></p> 3 <h2>Hello, <input data-bind="value: fullName"/>!</h2>
這里使用data-bind分別對firstName、lastName和fullName進行了綁定,這樣我們就可以看到各個屬性的值了。
六、為什么使用組合屬性(Computed Observables)
在使用組合屬性(Computed Observables)時,我們可以對我們想要處理的屬性或者驗證做出相應的措施,比如:
(1)、值的轉換
我們可以通過以下的代碼將對應的輸入表示為對應的金額輸出到頁面:
1 <script type="text/javascript" src="knockout-2.2.0.js"></script> 2 3 <p>Enter bid price: <input data-bind="value: formattedPrice"/></p> 4 5 <script type="text/javascript"> 6 function MyViewModel() { 7 this.price = ko.observable(25.99); 8 9 this.formattedPrice = ko.computed({ 10 read: function () { 11 return '¥' + this.price().toFixed(2); 12 }, 13 write: function (value) { 14 value = parseFloat(value.replace(/[^\.\d]/g, "")); 15 this.price(isNaN(value) ? 0 : value); 16 }, 17 owner: this 18 }); 19 } 20 21 ko.applyBindings(new MyViewModel()); 22 </script>
(2)、用戶輸入驗證,只允許輸入數字
1 <script type="text/javascript" src="knockout-2.2.0.js"></script> 2 3 <p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p> 4 <div data-bind="visible: !lastInputWasValid()">That's not a number!</div> 5 6 <script type="text/javascript"> 7 function MyViewModel() { 8 this.acceptedNumericValue = ko.observable(123); 9 this.lastInputWasValid = ko.observable(true); 10 11 this.attemptedValue = ko.computed({ 12 read: this.acceptedNumericValue, 13 write: function (value) { 14 if (isNaN(value)) 15 this.lastInputWasValid(false); 16 else { 17 this.lastInputWasValid(true); 18 this.acceptedNumericValue(value); 19 } 20 }, 21 owner: this 22 }); 23 } 24 25 ko.applyBindings(new MyViewModel()); 26 </script>
以上只是個人見解,還望大家多多指教。