2012-04-24 3 views
6

У меня есть массив предметов, возвращающихся из службы. Я пытаюсь определить вычисляемый наблюдаемый для каждого экземпляра элемента, поэтому мой инстинкт подсказывает, чтобы я положил его на прототип.Вычислены ли наблюдаемые на прототипах возможности в KnockoutJS?

Один случай для вычисленного наблюдаемого: система вычисляет точки, но пользователь может выбрать переопределить вычисленное значение. Мне нужно сохранить рассчитанное значение в случае, если пользователь удалит переопределение. Мне также необходимо объединить назначенные пользователем и рассчитанные точки и добавить итоговые значения.

Я использую отображение, чтобы сделать следующее:

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() в сопоставлении, я мог бы захватить контекст, но тогда есть экземпляр метода для каждого экземпляра объекта.

Я нашел сообщение Группы Google Майкла Беста (http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13). Прототип возвращает новый вычисленный наблюдаемый «активировать». Я не понял, что вызывает «активировать» (может быть, Objs?), Но я предполагаю, что это все равно происходит один раз за объект, и я не знаю, какой объем «этого» получит.

На данный момент, я считаю, что я прошел мимо того, что доступно в опубликованных документах, но я все еще работаю над расшифровкой того, что происходит от источника.

ответ

7

Вы упомянули, что вы не хотите иметь экземпляр функции ko.computed для каждого экземпляра вашего класса javascript, однако на самом деле это не будет работать с тем, как была создана функциональность ko. Когда вы используете ko.computed или ko.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 не позволит мне сохранить фактическую вычисленную наблюдаемую на прототипе. –

+0

eric, не могли бы вы предоставить более «сахарную» версию? Что-то вроде childClass = BaseClass.extend (function() {/*...*/}), который уже устанавливает цепочку прототипов? Необходимость делать это вручную для каждого экземпляра класса немного странно ... – fbuchinger

+0

Я не вижу причин, по которым вы не могли бы поставить статическую функцию 'extend' на унаследованном Constructor, если она в основном это делает: ' SubClass.extend = function (extender) {SubClass.prototype.extend (расширитель); }; ' – ericb

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