计算属性
Knockout.js隨手記(1) 開篇已經(jīng)示范過最簡單的應用,為<input>與<span>加上data-bind宣告,透過 ko.applyBindings()綁定到定義好的ViewModel上,當input改變輸入內(nèi)容,會立即反應在span。然而,有些場合數(shù)據(jù)需經(jīng)加 工才能使用,例如: 指定日期格式,將數(shù)字相加... 等等,此時ko.computed()便派上用場。
使用ko.computed() 時,最簡單的做法是直接傳入function,在其中引用其他的ViewModel屬性處理運算后傳回結果;因knockout具備強大的依賴關系追蹤能 力,能記下你引用了哪些屬性,一旦被引用的屬性來源改變,便會自動調(diào)用ko.computed()計算新值。
范例1
首先我們創(chuàng)建個ViewModel, 然后 將使用計算屬性將結果返回給fullName.
function AppViewModel() {
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
},
this);
}
ko.applyBindings(new AppViewModel());
現(xiàn)在我們做的事情就是綁定這些值
<p>First name: <input data-bind="value: firstName"/></p>
<p>Last name: <input data-bind="value: lastName"/></p>
<h2>Hello, <span data-bind="text: fullName"/>!</h2>
我們來查看下運行效果:
注意:由于this在不同的范圍內(nèi)又不同的含義,往往為了避免混淆,我們采用一項流行的約定,就是把這個this指向另一個習慣性的變量(self),我們的代碼可以修改為如下:
<body>
<p>
First name:
<input data-bind="value: firstName" />
</p>
<p>
Last name:
<input data-bind="value: lastName" />
</p>
<h2>
Hello,
<span data-bind="text: fullName" />
!
</h2>
<script type="text/javascript">
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();
},
this);
}
ko.applyBindings(new AppViewModel());
</script>
</body>
范例2
可能你和我一樣在想,既然knockout支持依賴性追蹤特性,那么,我可以通過更改fullName的值去動態(tài)修改first Name和 Last Name嗎?
那么我們做的工作就是分解FullName的輸入值。
<p>First name: <input data-bind="value: firstName"/></p>
<p>Last name: <input data-bind="value: lastName"/></p>
<h2>Hello, <input data-bind="value: fullName"/>!</h2>
<script type="text/javascript">
function MyViewModel() {
var self = this;
self.firstName = ko.observable('Planet');
self.lastName = ko.observable('Earth');
self.fullName = ko.computed({
read: function () {
return self.firstName() + " " + self.lastName();
},
write: function (value) {
var lastSpacePos = value.lastIndexOf(" ");
if (lastSpacePos > 0) { // Ignore values with no space character
self.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
self.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
}
},
owner: self
});
}
ko.applyBindings(new MyViewModel());
</script>
代碼很簡單,可以說也是最常見的js截取字符串的應用,我們只要注意其中的三個代理功能:read,write,owner就可以,實現(xiàn)的效果如下:
范例3
這例子其實算是對范例2的一個復習,主要功能是提供金額格式的自動轉(zhuǎn)換(包括精度和格式)已經(jīng)垃圾字符的過濾
<p>
Enter bid price:
<input data-bind="value: formattedPrice" />
</p>
<br/>
<script type="text/javascript">
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());
</script>
不管用戶什么時候輸入新價格,輸入什么格式,text box里會自動更新為帶有2位小數(shù)點和貨幣符號的數(shù)值。這樣用戶可以看到你的程序有多聰明,來告訴用戶只能輸入2位小數(shù),否則的話自動刪除多余的位數(shù),當 然也不能輸入負數(shù),因為write的callback函數(shù)會自動刪除負號。
我們來查看下運行效果:
范例4 過濾并驗證用戶輸入
<p>Enter a numeric value: <input data-bind="value: attemptedValue"/>
<span data-bind="visible:lastInputWasValid()">驗證通過!</span>
</p>
<div data-bind="visible: !lastInputWasValid()">這不是一個合法的數(shù)字!</div>
<script type="text/javascript">
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());
</script>
運行效果:
依賴跟蹤是如何工作的
新手沒必要知道太清楚,但是高級開發(fā)人員可以需要知道為什么依賴監(jiān)控屬性能夠自動跟蹤并且自動更新UI…
事實上,非常簡單,甚至說可愛。跟蹤的邏輯是這樣的:
1. 當你聲明一個依賴監(jiān)控屬性的時候,KO會立即調(diào)用執(zhí)行函數(shù)并且獲取初始化值。
2. 當你的執(zhí)行函數(shù)運行的時候,KO會把所有需要依賴的依賴屬性(或者監(jiān)控依賴屬性)都記錄到一個Log列表里。
執(zhí)行函數(shù)結束以后,KO會向所有Log里需要依賴到的對象進行訂閱。訂閱的callback函數(shù)重新運行你的執(zhí)行函數(shù)。然后回頭重新執(zhí)行上面的第一步操作(并且注銷不再使用的訂閱)。
3.最后KO會通知上游所有訂閱它的訂閱者,告訴它們我已經(jīng)設置了新值。
4. 所有說,KO不僅僅是在第一次執(zhí)行函數(shù)執(zhí)行時候探測你的依賴項,每次它都會探測。舉例來說,你的依賴屬性可以是動態(tài)的:依賴屬性A代表你是否依賴于依賴屬 性B或者C,這時候只有當A或者你當前的選擇B或者C改變的時候執(zhí)行函數(shù)才重新執(zhí)行。你不需要再聲明其它的依賴:運行時會自動探測到的。
另 外一個技巧是:一個模板輸出的綁定是依賴監(jiān)控屬性的簡單實現(xiàn),如果模板讀取一個監(jiān)控屬性的值,那模板綁定就會自動變成依賴監(jiān)控屬性依賴于那個監(jiān)控屬性,監(jiān) 控屬性一旦改變,模板綁定的依賴監(jiān)控屬性就會自動執(zhí)行。嵌套的模板也是自動的:如果模板X render模板 Y,并且Y需要顯示監(jiān)控屬性Z的值,當Z改變的時候,由于只有Y依賴它,所以只有Y這部分進行了重新繪制(render)。
范例5
knocut自動依賴性跟蹤通常不正是您想要。但你有時可能需要控制觀測值將更新計算觀察到的,尤其是如果計算可觀察執(zhí)行某種操作,如一個Ajax請求。peek函數(shù),可以讓你訪問到監(jiān)控屬性或計算屬性,而無需創(chuàng)建一個依賴。
ko.computed(function() {
var params = {
page: this.pageIndex(),
selected: this.selectedItem.peek()
};
$.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);
其中selectItem屬性使用了Peek函數(shù),所以計算屬性會隨時監(jiān)控和更新PageIndex的值,但它忽略更改selectItem。
總結
- 上一篇: 2021年陕西省中考数学试题及答案(20
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?