原文地址:http://knockoutjs.com/documentation/extenders.html
原文名稱:Using extenders to augment observables
在值發生變化的時候,Knockout 的可觀察對象提供了基本的功能來支持讀/寫,以及通知訂閱者。在有些情況下,你可能希望能為可觀察對象添加一些功能. 包括增加一些屬性,或者通過為可觀察對象增加寫入的附加處理, Knockout 擴展器提供了一種簡單並且靈活的途徑,支持實現參數化的可觀察對象。
如何創建一個擴展器
創建擴展器涉及到為 ko.extenders 對象添加一個函數,在這個函數被調用的時候,可觀察對象將作為第一個參數,其它的選項被作為第二個參數。函數既可以返回可觀察對象本身,也可以返回通過某種方式創建的新的可觀察對象。
下面是一個簡單的例子,logChange 擴展訂閱了主題對象,提供了一個可配置的提示信息,在可觀察對象發生變化的時候,在控制台輸出這個提示信息和可觀察對象的最新值。
ko.extenders.logChange = function(target, option) { target.subscribe(function(newValue) { console.log(option + ": " + newValue); }); return target; };
通過在可觀察對象上調用 extend 函數可以使用這個擴展,extend 的參數是一個對象,其中名為 logChange 的屬性表示使用這個擴展,屬性的值就是擴展函數的第二個參數的值。
this.firstName = ko.observable("Bob").extend({logChange: "first name"});
這樣,如果 firstName
可觀察對象的值修改為 Ted ,就會在控制台輸出 first name: Ted
.
示例1:強制輸入數字
這個示例創建一個擴展強制對可觀察對象只能提供帶有指定小數位數的數字。在這里,擴展器創建了一個新的允許寫的支持計算的可觀察對象,注入在真正的可觀察對象之前進行寫入處理。
源代碼如下所示:
<p><input data-bind="value: myNumberOne" /> (round to whole number)</p> <p><input data-bind="value: myNumberTwo" /> (round to two decimals)</p>
擴展的源代碼和模型如下:
ko.extenders.numeric = function(target, precision) { //create a writeable computed observable to intercept writes to our observable var result = ko.computed({ read: target, // 返回源對象的值 write: function(newValue) { var current = target(), roundingMultiplier = Math.pow(10, precision), newValueAsNum = isNaN(newValue) ? 0 : parseFloat(+newValue), valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier; // 如果發生變化了,寫入源對象 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); } } } }); //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));
示例中2:為可觀察對象添加驗證
這個示例為可觀察對象添加一個必須提供的驗證擴展。不會返回新對象,這個擴展為可擴展對象添加了一個子可觀察對象。由於可觀察對象實際上是一個函數,可以擁有真正的屬性。然而,當視圖模型轉換為 JSON 的時候,子可觀察對象將會被丟棄掉,而留下真正的可觀察對象的值。對於僅僅與 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) { // 添加子可觀察對象 target.hasError = ko.observable(); target.validationMessage = ko.observable(); // 執行驗證的函數 function validate(newValue) { target.hasError(newValue ? false : true); target.validationMessage(newValue ? "" : overrideMessage || "This field is required"); } // 初始驗證 validate(target()); // 當值發生變化的時候進行驗證 target.subscribe(validate); // 返回源可觀察對象 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"));
應用多個擴展
也可以對可觀察對象應用多個擴展。
this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" });
在這個例子中,可觀察對象應用了兩個擴展。