6

我有一系列从服务返回的项目。我试图为每个 Item 实例定义一个计算的 observable,所以我的直觉告诉我把它放在原型上。

计算 observable 的一种情况:系统计算点,但用户可以选择覆盖计算值。我需要保持计算值可用,以防用户删除覆盖。我还需要合并用户分配和计算的点,并将总数加起来。

我正在使用映射来执行以下操作:

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);

它现在的工作方式,我必须调用匿名函数来返回计算的 observable。这似乎为每个绑定创建了一个计算的 observable 的新实例,这违背了将其放在原型上的大部分要点。每次访问 observable 时都必须破译使用多少个括号,这有点烦人。

它也有些脆弱。如果我尝试在代码中访问 Points(),我不能这样做

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

因为这会将 Points() 的上下文更改为 DOMWindow,而不是 item。

如果我将计算结果放在映射中的 create() 中,我可以捕获上下文,但是每个对象实例上都有该方法的副本。

我找到了 Michael Best 的 Google Groups 帖子 ( http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13 )。原型在“激活”时返回一个新的计算 observable。我还没有弄清楚什么叫做“激活”(也许是 Objs?),但我猜它仍然会每个对象发生一次,而且我不知道“这个”会得到什么范围。

在这一点上,我相信我已经超越了已发布文档中可用的内容,但我仍在努力从源头上破译正在发生的事情。

4

3 回答 3

7

你提到你不想在ko.computed你的 javascript 类的每个实例上都有一个函数的实例,但是,这不会真正适用于 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();

然后对于计算出的 observables(必须是你的每个实例上的实例函数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每个实例的逻辑。

于 2012-04-25T00:56:11.773 回答
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;
});

};

于 2015-02-13T13:58:31.437 回答
0
Item.prototype.extend(function () {
var scope = this
this.Points = ko.computed(function () {
    return (this.PointsFromUser != null) ? 
           this.PointsFromUser : this.PointsCalculated;
}, scope);
};
于 2015-02-13T14:06:12.850 回答