Knockout observables提供了支持讀取/寫入值並在值改變時通知訂閱者所需的基本功能。 但在某些狀況下,您可能但願向可觀察者添加其餘功能。 這可能包括經過在可觀察者前面放置一個可寫的計算可觀察符來向可觀察或截取寫入添加額外的屬性。 敲除擴展器提供了一種簡單和靈活的方式來對可觀察者進行這種類型的擴充。css
建立擴展器涉及向ko.extenders對象添加一個函數。 函數接受observable自己做爲第一個參數和第二個參數中的任何選項。 而後它能夠返回observable或返回一些新的像一個計算的observable,它以某種方式使用原來的observable。服務器
這個簡單的logChange擴展器訂閱了observable,並使用控制檯寫入任何更改以及可配置的消息。app
ko.extenders.logChange = function(target, option) { target.subscribe(function(newValue) { console.log(option + ": " + newValue); }); return target; };
您將經過調用observable的extend函數並傳遞包含日誌Change屬性的對象來使用此擴展器。ide
this.firstName = ko.observable("Bob").extend({logChange: "first name"});
若是firstName observable值更改成Ted,那麼控制檯將顯示名字:Ted。函數
此示例建立一個擴展器,該擴展器強制對可觀察對象的寫入被四捨五入到可配置的精度級別。 在這種狀況下,擴展器將返回一個新的可寫的計算可觀察,它將位於真正的可觀察的攔截寫入以前。ui
(round to whole number)this
(round to two decimals)spa
UI源碼:日誌
<p><input data-bind="value: myNumberOne" /> (round to whole number)</p> <p><input data-bind="value: myNumberTwo" /> (round to two decimals)</p>
視圖模型源碼:code
ko.extenders.numeric = function(target, precision) { //create a writable computed observable to intercept writes to our observable var result = ko.pureComputed({ read: target, //always return the original observables value write: function(newValue) { var current = target(), roundingMultiplier = Math.pow(10, precision), newValueAsNum = isNaN(newValue) ? 0 : +newValue, valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier; //only write if it changed if (valueToWrite !== current) { target(valueToWrite); } else { //if the rounded value is the same, but a different value was written, force a notification for the current field if (newValue !== current) { target.notifySubscribers(valueToWrite); } } } }).extend({ notify: 'always' }); //initialize with current value to make sure it is rounded appropriately result(target()); //return the new computed observable return result; }; function AppViewModel(one, two) { this.myNumberOne = ko.observable(one).extend({ numeric: 0 }); this.myNumberTwo = ko.observable(two).extend({ numeric: 2 }); } ko.applyBindings(new AppViewModel(221.2234, 123.4525));
注意,爲了自動從UI中刪除被拒絕的值,有必要在計算的observable上使用.extend({notify:'always'})。 沒有這個,用戶可能輸入一個無效的newValue,當四捨五入獲得一個不變的valueToWrite。 而後,因爲模型值不會改變,所以在UI中沒有更新文本框的通知。 使用{notify:'always'}會致使文本框刷新(刪除被拒絕的值),即便計算的屬性沒有更改值。
此示例建立一個擴展器,容許將observable標記爲必需。 這個擴展器不是返回一個新的對象,而是簡單地向現有的observable添加額外的子可觀察量。 由於observables是函數,它們實際上能夠有本身的屬性。 可是,當視圖模型轉換爲JSON時,子可觀察項將被刪除,咱們將只剩下咱們的實際observable的值。 這是一個很好的方法來添加只與UI相關的附加功能,而不須要發送回服務器。
UI源碼:
<p data-bind="css: { error: firstName.hasError }"> <input data-bind='value: firstName, valueUpdate: "afterkeydown"' /> <span data-bind='visible: firstName.hasError, text: firstName.validationMessage'> </span> </p> <p data-bind="css: { error: lastName.hasError }"> <input data-bind='value: lastName, valueUpdate: "afterkeydown"' /> <span data-bind='visible: lastName.hasError, text: lastName.validationMessage'> </span> </p>
視圖模型暈嗎:
ko.extenders.required = function(target, overrideMessage) { //add some sub-observables to our observable target.hasError = ko.observable(); target.validationMessage = ko.observable(); //define a function to do validation function validate(newValue) { target.hasError(newValue ? false : true); target.validationMessage(newValue ? "" : overrideMessage || "This field is required"); } //initial validation validate(target()); //validate whenever the value changes target.subscribe(validate); //return the original observable return target; }; function AppViewModel(first, last) { this.firstName = ko.observable(first).extend({ required: "Please enter a first name" }); this.lastName = ko.observable(last).extend({ required: "" }); } ko.applyBindings(new AppViewModel("Bob","Smith"));
多個擴展器能夠在對可觀察者的.extended方法的單個調用中應用。
this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" });
在這種狀況下,required和logChange擴展器都會對咱們的observable執行。