2012-04-24 23 views
6

我有一個從服務返回的項目數組。我試圖爲每個Item實例定義一個計算的observable,所以我的直覺告訴我把它放在原型上。在KnockoutJS中是否可以在原型上計算observables?

計算觀察值的一種情況:系統計算點數,但用戶可以選擇覆蓋計算值。我需要保持計算值可用,以防用戶刪除覆蓋。我還需要合併用戶分配的和計算的分數,並將總計加起來。

我使用映射做到以下幾點:

var itemsViewModel; 
var items = [ 
    { 'PointsCalculated' : 5.1 }, 
    { 'PointsCalculated' : 2.37, 'PointsFromUser' : 3 } 
]; 

var mapping = { 
    'Items' : { 
     create : function(options) { 
      return new Item(options.data); 
     } 
    } 
}; 

var Item = function(data) { 
    var item = this; 
    ko.mapping.fromJS(data, mapping, item); 
}; 

Item.prototype.Points = function() { 
    var item = this; 
    return ko.computed(function() { 
     // PointsFromUser may be 0, so only ignore it if the value is undefined/null 
     return (item.PointsFromUser != null) ? item.PointsFromUser : item.PointsCalculated; 
    }); 
}; 

ko.mapping.fromJS(items, mapping, itemsViewModel); 

現在的工作方式,我要調用匿名函數返回計算的觀測。這似乎爲每個綁定創建了一個計算可觀察值的新實例,這個實例大部分都是將其放在原型上。每次訪問可觀察數據時,要解密多少個括號纔有點煩人。

它也有點脆弱。如果我試圖接入點()的代碼,我不能做

var points = 0; 
var p = item.Points; 
if (p && typeof p === 'function') { 
    points += p(); 
} 

因爲更改爲點()來DOMWindow的背景下,而不是項目。

如果我把計算器放在映射中的create()中,我可以捕獲上下文,但是在每個對象實例上都有一個方法的副本。

我找到Michael Best的Google Groups帖子(http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13)。原型在「激活」時返回一個新的計算可觀察值。我還沒有弄清楚什麼叫「激活」(也許是Objs?),但是我猜測它每個對象都會發生一次,我不知道'this'會得到什麼範圍。

在這一點上,我相信我已經超越了發佈文檔中的可用內容,但我仍在努力破譯來自源代碼的內容。

回答

7

你提到你不希望在你的javascript類的每個實例上都有一個ko.computed函數的實例,但是,這對於如何構建ko的功能並不適用。當你使用ko.computedko.observable時,他們創建了特定的內存指針指向私有變量,你通常不希望在類實例中共享(儘管極少數情況下可能)。

我做這樣的事情:

var Base = function(){ 
    var extenders = []; 

    this.extend = function(extender){ 
     extenders.push(extender); 
    }; 

    this.init = function(){ 
     var self = this; // capture the class that inherits off of the 'Base' class 

     ko.utils.arrayForEach(extenders, function(extender){ 

      // call each extender with the correct context to ensure all 
      // inheriting classes have the same functionality added by the extender 
      extender.call(self); 
     }); 
    }; 
}; 

var MyInheritedClass = function(){ 
    // whatever functionality you need 

    this.init(); // make sure this gets called 
}; 

// add the custom base class 
MyInheritedClass.prototype = new Base(); 

然後計算觀測(這必須在你的MyInheritedClass的每個實例的實例函數)我只是宣佈他們在extender像這樣:

MyInheritedClass.prototype.extend(function(){ 

    // custom functionality that i want for each class 
    this.something = ko.computed(function() { 
     return 'test'; 
    }); 
}); 

鑑於您的例子,上面定義的Base類,你可以很容易做到:

var Item = function(data) { 
    var item = this; 

    ko.mapping.fromJS(data, mapping, item); 

    this.init(); // make sure this gets called 
}; 
Item.prototype = new Base(); 

Item.prototype.extend(function() { 
    var self = this; 

    this.Points = ko.computed(function() { 

     // PointsFromUser may be 0, so only ignore it if the value is undefined/null 
     return (self.PointsFromUser != null) ? 
       self.PointsFromUser : self.PointsCalculated; 
    }); 
}; 

然後,Item類的所有實例都將具有Points屬性,並且它將正確地處理每個實例的ko.computed邏輯。

+0

謝謝。我認爲.extend()的目的就是點擊了。這很好地解決了我的範圍/雙重函數調用問題,我會告訴你Knockout不會讓我在原型上保留實際的計算可觀察性。 – 2012-04-26 02:53:30

+0

eric,你能否提供更多的「糖化」版本?像childClass = BaseClass.extend(function(){/*...*/}),它已經做了原型鏈設置?必須爲每個類實例手動執行它有點奇怪... – fbuchinger 2012-10-17 11:23:48

+0

我看不出有什麼理由不能在繼承的構造函數上放置'擴展'靜態函數,只要它基本上: ' SubClass.extend = function(extender){SubClass.prototype.extend(extender); };' – ericb 2012-10-17 20:25:30

0
Item.prototype.Points = function() { 
var item = this; 
return ko.computed(function() { 
    // PointsFromUser may be 0, so only ignore it if the value is undefined/null 
    return (item.PointsFromUser != null) ? item.PointsFromUser : item.PointsCalculated; 
}); 

};

0
Item.prototype.extend(function() { 
var scope = this 
this.Points = ko.computed(function() { 
    return (this.PointsFromUser != null) ? 
      this.PointsFromUser : this.PointsCalculated; 
}, scope); 
}; 
相關問題