Knockout 新版應用開發教程之Computed Observables

Computed Observables

若是你有監控屬性firstName和lastName的話,此時若是你想要顯示全名?html

這個時候computed(之前叫作依賴)監控屬性就出馬了,這是一個函數用來依賴一個或者多個監控屬性,而且當其中的任何一個依賴對象被改變的時候都將會自動更新。程序員

例如,view model類算法

function AppViewModel() {
    this.firstName = ko.observable('Bob');
    this.lastName = ko.observable('Smith');
}

你能夠增長一個computed計算依賴的來獲得一個全名編程

function AppViewModel() {
    // ... leave firstName and lastName unchanged ...
 
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
}

如今你能夠綁定到它的UI元素,e.g數組

The name is <span data-bind="text: fullName"></span>

firstName或lastName發生改變它都會更新(無論誰改變,執行函數都會調用一次,無論改變成什麼,他的值都會更新到UI或者其餘依賴監控屬性上)服務器

管理this

初學者跳過,你只須要安裝上面例子中的代碼模式寫就好了,無需知道/關注這個this。閉包

ko.computed的第二個參數是什麼,你是否很疑惑?app

在前面的代碼,咱們在定義computed依賴的時候用到了this,沒有它,將不可以引用到this.firstName()this.lastName()。函數

老練的Javascript程序員就以爲很日常,可是假如不怎麼了解Javascript就會以爲難以理解(如C#和Java程序員不須要設置此值,但JavaScript呢,做用域是能夠被改變的)性能

A popular convention that simplifies things

能夠用一個簡單的辦法去簡化這種行爲

這是一種比較流行的辦法用於避免追蹤this:

若是你的模型的構造函數複製一個引用this到一個不一樣的變量(一般稱爲self),而後你能夠用self的在你的模型和沒必要擔憂它被從新定義指的是別的東西。

好比說

function AppViewModel() {
    var self = this;
 
    self.firstName = ko.observable('Bob');
    self.lastName = ko.observable('Smith');
    self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });
}

由於self是在函數的閉包中被捕獲,在任何嵌套函數仍然是同一個,例如ko.computed的evaluator,當你設計到事件句柄的時候這個技巧更有用,能夠看看更多的例子live examples.

Dependency chains just work

依賴鏈的工做

固然,你但願你能建立一個計算監控屬性鏈,例如,你能夠這樣

  • 監控屬性items表述一組列表項
  • 監控屬性selectedIndexes保存着被用戶選上的列表項的索引
  • 依賴監控屬性selectedItems 返回的是selectedIndexes 對應的列表項數組
  • 另外一個依賴監控屬性返回的true或false依賴於 selectedItems 的各個列表項是否包含一些屬性(例如,是否新的或者還未保存的)。一些UI element(像按鈕的啓用/禁用)的狀態取決於這個值)。
  • 而後,items或者selectedIndexes 的改變將會影響到全部依賴監控屬性的鏈,全部綁定這些屬性的UI元素都會自動更新。多麼整齊與優雅!

可寫的計算監控屬性

初學者跳過,可寫的computed observables是比較高級的了,在大多狀況下都是用不到的

當你學到了,計算監控屬性的值是經過計算其餘監控屬性獲得,在感受上計算監控屬性正常狀況下僅僅是隻讀的。

這樣看起來很奇怪,那麼,computed observables是否能夠支持可寫呢?

你只須要提供本身的回調函數作一些事情,在寫值的時候。

Example 1: Decomposing user input

分解用戶的輸入

返回到經典的 「first name + last name = full name」 實例,你能夠把事情返過來看:

fullName給計算監控屬性寫入寫東西,因此你能直接編輯出全名,讓用戶直接輸入姓名全稱,而後輸入的值將被解析並映射寫入到基本的監控屬性firstName和lastName上:

function MyViewModel() {
    this.firstName = ko.observable('Planet');
    this.lastName = ko.observable('Earth');
 
    this.fullName = ko.computed({
        read: function () {
            return this.firstName() + " " + this.lastName();
        },
        write: function (value) {
            var lastSpacePos = value.lastIndexOf(" ");
            if (lastSpacePos > 0) { // Ignore values with no space character
                this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
                this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
            }
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

在這裏例子,write的回調句柄傳入了一個值被分解後傳入到「firstName」 和「lastName」  上,而且寫的那些值會返回給相關的監控屬性

跟日常同樣將這個view model綁定到DOM元素上,以下:

<p>First name: <span data-bind="text: firstName"></span></p>
<p>Last name: <span data-bind="text: lastName"></span></p>
<h2>Hello, <input data-bind="value: fullName"/>!</h2>

這是一個Hello World 例子的反例子,姓和名都不可編輯,相反姓和名組成的姓名全稱倒是可編輯的。

以前的視圖模型的代碼演示單個參數的語法是爲了初始化計算監控屬性,看computed observable reference 如下所有可用選項的列表

Example 2: A value converter

轉化一個value

有時候你可能須要顯示一些不一樣格式的數據,從基礎的數據轉化成顯示格式。

好比,你存儲價格爲float類型,可是容許用戶編輯的字段須要支持貨幣單位和小數點。

你能夠用可寫的依賴監控屬性來實現,而後解析傳入的數據到基本 float類型裏:

 

function MyViewModel() {
    this.price = ko.observable(25.99);
 
    this.formattedPrice = ko.computed({
        read: function () {
            return '$' + this.price().toFixed(2);
        },
        write: function (value) {
            // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
            value = parseFloat(value.replace(/[^\.\d]/g, ""));
            this.price(isNaN(value) ? 0 : value); // Write to underlying storage
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

而後咱們綁定formattedPrice到text box上:

<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>

因此,無論用戶何時輸入新價格,輸入什麼格式,text box裏會自動更新爲帶有2位小數點和貨幣符號的數值。

這樣用戶能夠看到你的程序有多聰明,來告訴用戶只能輸入2位小數,不然的話自動刪除多餘的位數,固然也不能輸入負數,由於write的callback函數會自動刪除負號。

Example 3: Filtering and validating user input

過濾並驗證用戶輸入

例1展現的是寫操做過濾的功能,若是你寫的值不符合條件的話將不會被寫入,忽略全部不包括空格的值。

再多走一步,你能夠聲明一個監控屬性isValid 來表示最後一次寫入是否合法,而後根據真假值顯示相應的提示信息。

稍後仔細介紹,先參考以下代碼:

function MyViewModel() {
    this.acceptedNumericValue = ko.observable(123);
    this.lastInputWasValid = ko.observable(true);
 
    this.attemptedValue = ko.computed({
        read: this.acceptedNumericValue,
        write: function (value) {
            if (isNaN(value))
                this.lastInputWasValid(false);
            else {
                this.lastInputWasValid(true);
                this.acceptedNumericValue(value); // Write to underlying storage
            }
        },
        owner: this
    });
}
 
ko.applyBindings(new MyViewModel());

… 按照以下格式聲明綁定元素:

<p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p>
<div data-bind="visible: !lastInputWasValid()">That's not a number!</div>

如今,acceptedNumericValue 將只接受數字,其它任何輸入的值都會觸發顯示驗證信息,而會更新acceptedNumericValue。

備註:上面的例子顯得殺傷力太強了,更簡單的方式是在<input>上使用jQuery Validation和number class。

Knockout能夠和jQuery Validation一塊兒很好的使用,參考例子:grid editor

固然,上面的例子依然展現了一個如何使用自定義邏輯進行過濾和驗證數據,若是驗證很複雜而jQuery Validation很難使用的話,你就能夠用它。

How dependency tracking works

依賴跟蹤如何工做的

初學者能夠跳過,可是高級開發人員能夠須要知道爲何依賴監控屬性可以自動跟蹤而且自動更新UI…

其實很簡單並且可愛的,這個跟蹤的算法是這樣的:

  1. 當你聲明一個依賴監控屬性的時候,KO會當即調用執行函數而且獲取初始化值。
  2. 當你的執行函數運行的時候,KO會把任何在監控屬性(或者計算監控屬性))讀到的值都會都記錄到一個Log列表裏。
  3. 執行函數結束之後,KO會向全部Log裏須要依賴到的對象進行訂閱。訂閱的callback函數是從新運行你的執行函數。而後回頭從新執行上面的第一步操做(而且註銷再也不使用的訂閱)。
  4. 最後KO會通知全部訂閱它的訂閱者,告訴它們我已經設置了新值。

全部說,KO不只僅是在第一次執行函數執行時候探測你的依賴項,每次它都會探測。舉例來講,你的依賴屬性能夠是動態的:依賴屬性A表明你是否依賴於依賴屬性B或者C,這時候只有當A或者你當前的選擇B或者C改變的時候執行函數才從新執行。你不須要再聲明其它的依賴:運行時會自動探測到的。

另一個技巧是:一個模板輸出的綁定是依賴監控屬性的簡單實現,若是模板讀取一個監控屬性的值,那模板綁定就會自動變成依賴監控屬性依賴於那個監控屬性,監控屬性一旦改變,模板綁定的依賴監控屬性就會自動執行。

Controlling dependencies using peek

使用Peek控制依賴

Knockout’s自動跟蹤依賴一般下是你想要的。可是你可能有時候須要控制某一個監控屬性去更新你的計算依賴屬性,特別是若是你的計算依賴可執行一些操做,

好比Ajax請求,那麼peek函數就可以讓你訪問一個observable或者computed observable而不是建立一個依賴

在下面的例子,一個

ko.computed(function() {
    var params = {
        page: this.pageIndex(),
        selected: this.selectedItem.peek()
    };
    $.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);

註釋:加入你不想要依賴屬性太頻繁的更新,你能夠看 throttle extender.

Determining if a property is a computed observable

假若有一個屬性是依賴屬性

在一些場景中,若是你是處理一個依賴屬性它是有用的編程方式,Knockout提供一個應用函數

ko.isComputed 將會幫助你解決這些狀況

例如,數據從服務器返回回來,你能夠要排除依賴屬性

 

for (var prop in myObject) {
  if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) {
      result[prop] = myObject[prop];
  }
}

此外,Knockout 提供了相似的功能,可以對監控屬性和依賴屬性起到做用

  • ko.isObservable:返回true的,監控屬性,監控數組和全部的依賴屬性
  • ko.isWriteableObservable:返回true, 監控屬性,監控數組,和 可寫的依賴屬性

Computed Observable Reference

引用依賴屬性

一個依賴屬性能夠有下面的形式構造出來:

  1. ko.computed( evaluator [, targetObject, options] ) 最多見的狀況,這種形式的支持建立一個依賴屬性
    • evaluator — 一個函數,用來求出依賴屬性當前的值
    • targetObject — 就是回調函數中,引用當前的this,要指定做用域,詳情看managing this
    • options — 爲依賴屬性的配置更多的屬性
  2. ko.computed( options ) 建立一個依賴屬性,傳入的是一個單個對象:
    • read — 必選,一個用來執行取得依賴監控屬性當前值的函數。
    • write — 可選,若是聲明的依賴屬性是可寫的,那麼這個函數接受一個值,那麼其餘代碼將會試着寫入到依賴屬性,過自定義邏輯將值再寫入各個基礎的監控屬性上。
    • owner — 可選,若是聲明,它就是KO調用read或write的callback時用到的this。
    • deferEvaluation — 可選,假如是true,那麼依賴屬性的值你不能獲取,默認狀況下,依賴屬性獲取這個值的話會馬上建立
    • disposeWhen — 可選,待翻譯,等分析源碼的時候補上
    • disposeWhenNodeIsRemoved — 可選,待翻譯,等分析源碼的時候補上

依賴屬性可提供如下功能

  • dispose() — 手動配置依賴屬性,清除全部訂閱依賴,若是你想要中止依賴屬性,當正在更新或者想要清除依賴屬性的內存
  • extend(extenders) — 給依賴屬性擴展一些內容
  • getDependenciesCount() — 返回當前被依賴屬性依賴的數量
  • getSubscriptionsCount() — 返回當前依賴屬性的訂閱數量(或者從其餘計算機的依賴屬性或手動訂閱)
  • isActive() — 返回依賴屬性支持更新,若是沒有依賴關係,將是無效的
  • peek() — 返回當前沒有建立依賴關係的值(看 peek
  • subscribe( callback [,callbackTarget, event] ) — 手工註冊依賴通知

有些地方比較拗口,等看完源碼後就能補準翻譯了~~

相關文章
相關標籤/搜索