為了確保Server端及Client端的ViewModel一致,在專案中我使用T4自動產生對應C# Class及JavaScript function,如此可確保ASP.NET端拋出的ViewModel與Client端的ViewModel完全一致,雖然這點Knockout Mapping Plug-In也辦得到,但自己產生的JavaScript ViewModel還可以加上JavaScript Documentation註解,配合Visual Studio強大的JS Intellisense功能,爽度是無法相比滴~
但有個小問題: 除了自動產生的屬性外,常會因開發需要還得額外加入UI控制用途的ko.observable或ko.computed,這部分在開發過程常會機動修改調整,不適合綁進自動產生程序,事後外加是較好抉擇。一開始我想得天真,以為用vm.prototype.anotherProp就可輕鬆搞定,後來卻發現這招處理ko.observable可行,遇到ko.computed時會因無法存取當下Instance而陷入困境。在ko.computed函數中,我們無法透過this取得當時物件,而宣告prototype時物件個體尚不存在,故也不可能當成ko.computed的第二個參數傳入,結果空有ko.computed卻無法依賴其他屬性進行運算。
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js">
</script>
<meta charset=utf-8 />
<title>KO範例27 - 擴充ViewModel(失敗)</title>
</head>
<body>
<input data-bind="value: foo" />
<div>fooPlusX = <span data-bind="text: fooPlusX"></span></div>
<script>
//透過CodeGen自動產生的ViewModel
function VMBoo() {
var self = this;
self.foo = ko.observable(1);
}
//事後想為ViewModel多加一個fooPlusX屬性,
//天真地想透過prototype加掛ko.computed直接搞定
VMBoo.prototype.fooPlusX = ko.computed(function() {
try {
//問題來了,在ko.computed中無從存取當時的instance
returnthis.foo() + "X";
}
catch (err) {
return err.message;
}
}); //instnace在此時當不存在,也無法當成參數傳給ko.computed
var vm = new VMBoo();
ko.applyBindings(vm);
</script>
</body>
</html>
在以上的失敗範例中,只會得到fooPlusX = Object [object global] has no method 'foo'的結果。線上展示
最後我找到一個解法,先在ViewModel的建構函式中埋下伏筆:
if (this.init) this.init(self);
而這個init函式可以透過vm.prototype.init = function(self) { … }加以定義,如此在init便能取得self(當時的物件個體)為所欲為,跟在建構式的寫法一致。線上展示
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js">
</script>
<meta charset=utf-8 />
<title>KO範例27 - 擴充ViewModel(成功)</title>
</head>
<body>
<input data-bind="value: foo" />
<div>fooPlusX = <span data-bind="text: fooPlusX"></span></div>
<script>
//透過CodeGen自動產生的ViewModel
function VMBoo() {
var self = this;
self.foo = ko.observable(1);
//CodeGen時額外呼叫透過prototype定義的init
if (this.init) this.init(self);
}
//宣告額外的init函式
VMBoo.prototype.init = function(self) {
self.fooPlusX = ko.computed(function() {
return self.foo() + "X";
});
};
var vm = new VMBoo();
ko.applyBindings(vm);
</script>
</body>
</html>
就醬,又排除掉偉大航道上的一個小障礙囉~
[KO系列]