2012-04-24 14 views
6

Tengo una serie de elementos que regresan del servicio. Intento definir un observable calculado para cada instancia de Item, por lo que mi instinto me dice que lo coloque en el prototipo.¿Son factibles los observables calculados en prototipos en KnockoutJS?

Un caso para el observable calculado: el sistema calcula puntos, pero el usuario puede elegir anular el valor calculado. Necesito mantener el valor calculado disponible en caso de que el usuario elimine la anulación. También necesito unir puntos asignados por el usuario y calculados, y sumar los totales.

estoy usando mapeo de hacer lo siguiente:

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

La forma en que funciona ahora, tengo que llamar a la función anónima para devolver la observables computarizada. Eso parece crear una nueva instancia de la observable calculada para cada enlace, que derrota la mayor parte del punto de ponerlo en el prototipo. Y es un poco molesto tener que descifrar cuántos paréntesis usar cada vez que accedo a un observable.

También es algo frágil. Si intento acceder Puntos() en el código, no puedo hacer

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

debido a que los cambios en el contexto de los puntos() para DOMWindow, en lugar de elemento.

Si pongo el calculado en create() en la asignación, podría capturar el contexto, pero luego hay una copia del método en cada instancia de objeto.

Encontré la publicación de Grupos de Google de Michael Best (http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13). El prototipo devuelve un nuevo observable calculado en "activar". No he descubierto lo que se llama "activar" (¿tal vez Objs?), Pero supongo que todavía sucede una vez por objeto, y no tengo ni idea de qué alcance 'obtendrá'.

En este punto, creo que ya supero lo que está disponible en los documentos publicados, pero todavía estoy trabajando para descifrar lo que está pasando desde la fuente.

Respuesta

7

Mencione que no desea tener una instancia de la función ko.computed en cada instancia de su clase javascript, sin embargo, eso no funcionará realmente con cómo se ha construido la funcionalidad de ko. Cuando usa ko.computed o ko.observable, crean punteros de memoria específicos para variables privadas dentro de las que normalmente no desearía compartir en instancias de clase (aunque en casos excepcionales podría hacerlo).

hago algo como esto:

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

luego por los observables computarizada (que tienen que ser funciones de instancia para cada instancia de su MyInheritedClass) acabo de declararlos en una extender así:

MyInheritedClass.prototype.extend(function(){ 

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

Dada su ejemplo y la clase Base definido anteriormente, fácilmente se podría hacer:

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

Luego todas las instancias de su clase Item tendrán una propiedad Points, y manejarán correctamente la lógica ko.computed por instancia.

+0

Gracias. Creo que el objetivo de .extend() es simplemente hacer clic. Esto resuelve mis problemas de invocación de alcance/función doble muy bien, y tomaré su palabra de que Knockout no me permitirá mantener la observación computable real en el prototipo. –

+0

eric, ¿podría proporcionar una versión más "azucarada"? Algo como childClass = BaseClass.extend (function() {/*...*/}) que ya hace la configuración de la cadena del prototipo? Tener que hacerlo manualmente para cada instancia de clase es un poco extraño ... – fbuchinger

+0

No veo ningún motivo por el que no se pueda poner una función estática 'extender' en el Constructor heredado, siempre y cuando básicamente lo haga: ' SubClass.extend = function (extensor) {SubClass.prototype.extend (extensor); }; ' – 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); 
}; 
Cuestiones relacionadas