若是你有監控屬性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呢,做用域是能夠被改變的)
能夠用一個簡單的辦法去簡化這種行爲
這是一種比較流行的辦法用於避免追蹤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.
依賴鏈的工做
固然,你但願你能建立一個計算監控屬性鏈,例如,你能夠這樣
初學者跳過,可寫的computed observables是比較高級的了,在大多狀況下都是用不到的
當你學到了,計算監控屬性的值是經過計算其餘監控屬性獲得,在感受上計算監控屬性正常狀況下僅僅是隻讀的。
這樣看起來很奇怪,那麼,computed observables是否能夠支持可寫呢?
你只須要提供本身的回調函數作一些事情,在寫值的時候。
分解用戶的輸入
返回到經典的 「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 如下所有可用選項的列表
轉化一個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函數會自動刪除負號。
過濾並驗證用戶輸入
例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很難使用的話,你就能夠用它。
依賴跟蹤如何工做的
初學者能夠跳過,可是高級開發人員能夠須要知道爲何依賴監控屬性可以自動跟蹤而且自動更新UI…
其實很簡單並且可愛的,這個跟蹤的算法是這樣的:
全部說,KO不只僅是在第一次執行函數執行時候探測你的依賴項,每次它都會探測。舉例來講,你的依賴屬性能夠是動態的:依賴屬性A表明你是否依賴於依賴屬性B或者C,這時候只有當A或者你當前的選擇B或者C改變的時候執行函數才從新執行。你不須要再聲明其它的依賴:運行時會自動探測到的。
另一個技巧是:一個模板輸出的綁定是依賴監控屬性的簡單實現,若是模板讀取一個監控屬性的值,那模板綁定就會自動變成依賴監控屬性依賴於那個監控屬性,監控屬性一旦改變,模板綁定的依賴監控屬性就會自動執行。
使用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.
假若有一個屬性是依賴屬性
在一些場景中,若是你是處理一個依賴屬性它是有用的編程方式,Knockout提供一個應用函數
ko.isComputed 將會幫助你解決這些狀況
例如,數據從服務器返回回來,你能夠要排除依賴屬性
for (var prop in myObject) { if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) { result[prop] = myObject[prop]; } }
此外,Knockout 提供了相似的功能,可以對監控屬性和依賴屬性起到做用
引用依賴屬性
一個依賴屬性能夠有下面的形式構造出來:
evaluator — 一個函數,用來求出依賴屬性當前的值
targetObject — 就是回調函數中,引用當前的this,要指定做用域,詳情看managing this
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] )
— 手工註冊依賴通知有些地方比較拗口,等看完源碼後就能補準翻譯了~~