Knockout教學1 - Knockout.js與MVVM模式簡介

Knockout.js與MVVM模式簡介

本篇教學會教讀者使用knockout.js的Model-View-ViewModel(MVVM)模式建立網頁UI的基礎知識。

讀者將學習如何定義UI外觀使用Views和宣告式繫結(declarative bindings)。它的資料和行為使用ViewModels和observables,和如何一切都保持在同步中自動由Knockout.js的相依追蹤(dependency tracking)(即使有任意鏈結(chain)的資料)。

在View使用繫結

下面,擁有一個包含個人資料的ViewModel。

// 這是一個簡單的ViewModel物件
function AppViewModel() {
    this.firstName = "Bert";
    this.lastName = "Bertington";
}

// 啟用Activates knockout.js
ko.applyBindings(new AppViewModel());

App物件包含兩個屬性,firstName與lastName。ko.applyBindings()是第一行knockout.js的程式碼,ko長knockout.js的關鍵字,applyBindings()方法是指定要繫結至那個ViewModel物件。

在View(專指呈現UI所在的地方,通常是指HTML)裡呈現個人資料,現在只呈現”todo”:

<!-- 這是View,HTML定義如何呈何UI -->
<p>姓: <strong>todo</strong></p>
<p>名: <strong>todo</strong></p>

修改View裡的<strong>元素,新增data-bind屬性來呈現個人名稱:

<p>姓: <strong data-bind="text: firstName"></strong></p>
<p>名: <strong data-bind="text: lastName"></strong></p>

data-bind屬性讓Knockout.js可以讓你以宣告方式將DOM元素與ViewModel屬性產生關聯。使用text繫結分配文字給DOM元素。

就這樣,我們完成了第一個有View有ViewModel的knockout.js網頁。

讓資料可編輯

當然,我們不限於呈現靜態資料。我們可以使用value繫結以及一些HTML元素,例如,<input>元素,使資料可以編輯。在View裡新增以下標籤:

<p>姓: <input data-bind="value: firstName" /></p>
<p>名: <input data-bind="value: lastName" /></p>

重新執行應用程式。我們已經可以編輯資料,編輯後沒有發生什麼事。當然,我們希望發生一些事…

簡介Observables

實際上,當你編輯這些文字時,它會更新ViewModel裡的資料,但是因為ViewModel的屬性只是純JavaScript字串(例如,this.firstName = "Bert";,"Bert"值是純字串),它們有沒有辦法通知任何人,他們改變了,所以UI保持靜態不變。這就是為什麼 Knockout.js具有observables(可觀察物件)的概念 — — 這些都是它們的值發生變動時,自動就會發出通知的屬性。

更新ViewModel,ko.observable()來觀察firstName與lastName屬性。

function AppViewModel() {
    // 純字串
    // this.firstName = "Bert";
    // this.lastName = "Bertington";
    
    // 使用可觀察物件
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");
}

重新執行應用程式。當我們完成修改離開輸入框時,相關連結的UI項目也會同步被knockout.js更新。

合併與計算

經常,我們被要求合併或轉換多個觀察的屬性值。以下範例會定義一個fullName,fullName格式是「firstName + "空格" + lastName」,在knockout.js要處理這種情況可使用computed(計算)屬性,它也是可觀察物件,它的計算是基於其他可觀察物件的值。

新增fullName屬性到ViewModel,然後進行computed計算:

this.fullName = ko.computed(function() {
    return this.firstName() + " " + this.lastName();    
}, this);

正如您所看到的,我們將回呼函數(callback function)傳遞給ko.computed指定它應該如何計算其值。下一步,呈現fullName值在你的UI裡:

<p>全名: <strong data-bind="text: fullName"></strong></p>

重新執行應用程式。當我們完成修改離開輸入框時,相關連結的UI項目(含fullName)也會同步被knockout.js更新。

如何運作

狀態能保持同步,是因為自動相依追蹤:最後一個<strong>相依於fullName,而fullName又相依於firstName和lastName,而fistName和lastName又可以通過編輯框來改變。任何的變更通過最小的物件關連變動來立即更新ViewModel與UI。

加入行為

我們為範例加上一個行為,設計一個按鈕,按鈕的內容可以讓姓和名變成全大寫。

首先加入一個uppercaseLastName方法到ViewModel裡,然後實作它:

this.uppercaseLastName = function() {
    var currentLastName = this.lastName();        // 讀取現值
    this.lastName(currentLastName.toUpperCase()); // 寫入修改後的值
};

注意,讀取或寫入可觀察物件的值,必須當成函式呼叫。

下一步,新增一個<button>元素到View,使用click繫結來關聯click事件與ViewModel裡的函式:

<button data-bind="click: uppercaseLastName">轉換為大寫</button>

重新執行應用程式。點擊【轉換為大寫】,你可以看到UI會立即更新lastName的狀態為全大寫。

以下為完整程式碼。

<!DOCTYPE html>
<html>
<head>
    <title>Knockout.js與MVVM模式簡介</title>
    <script src="knockout-2.1.0.js"></script>
    <script>
        function AppViewModel() {
            // 純字串
            // this.firstName = "Bert";
            // this.lastName = "Bertington";
            
            // 使用可觀察物件
            this.firstName = ko.observable("Bert");
            this.lastName = ko.observable("Bertington");
            
            // 計算
            this.fullName = ko.computed(function() {
                return this.firstName() + " " + this.lastName();    
            }, this);
            
            // 轉換大寫
            this.uppercaseLastName = function() {
                var currentVal = this.lastName();        // 讀取現值
                this.lastName(currentVal.toUpperCase()); // 寫入修改後的值
            };
        }

        ko.applyBindings(new AppViewModel());
    </script>
</head>
<body>
    <p>姓: <strong data-bind="text: firstName"></strong></p>
    <p>名: <strong data-bind="text: lastName"></strong></p>
    <p>全名: <strong data-bind="text: fullName"></strong></p>
    <br />
    <p>姓: <input data-bind="value: firstName" /></p>
    <p>名: <input data-bind="value: lastName" /></p>
    <button data-bind="click: uppercaseLastName">轉換為大寫</button>
</body>
</html>

4 則留言:

  1. 黑大最近也發了蠻多knockout的文
    兩位都有po文 這東西應該蠻有搞頭的

    回覆刪除
    回覆
    1. 無法和黑大比,人家是有程度的文章,我只是…

      刪除
  2. 金剛兄,經實測發現使用您的完整程式碼會產生錯誤,無法運行。
    第二個script,移到下面才OK。

    給您參考。
    以上
    James

    回覆刪除
    回覆
    1. 其實問題在 ko.applyBindings(new AppViewModel()); 因為是翻譯,所以就不異動它了。

      建議包在
      $(function(){
      ko.applyBindings(new AppViewModel());
      });

      等DOM元素都Ready後在進行Binding動作。

      刪除

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。