2012-04-24 3 views
6

Ich habe eine Reihe von Artikeln, die vom Dienst zurückkommen. Ich versuche, ein berechnetes Observable für jede Item-Instanz zu definieren, also sagt mein Instinkt, dass ich es auf den Prototyp setzen soll.Sind in KnockoutJS berechnete Observablen für Prototypen realisierbar?

Ein Fall für das berechnete Observable: Das System berechnet Punkte, aber der Benutzer kann den berechneten Wert überschreiben. Ich muss den berechneten Wert verfügbar halten, falls der Benutzer die Überschreibung entfernt. Ich muss auch benutzerdefinierte und berechnete Punkte zusammenführen und die Summen addieren.

I-Mapping bin mit der folgenden Funktionen ausführen:

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

So wie es jetzt funktioniert, ich habe die anonyme Funktion aufrufen, die berechnete beobachtbaren zurückzukehren. Das scheint für jede Bindung eine neue Instanz der berechneten Observablen zu schaffen, die den größten Teil des Punktes, an dem sie auf den Prototyp gesetzt wird, zunichte macht. Und es ist ein wenig lästig, zu entziffern, wie viele Klammern jedesmal benutzt werden müssen, wenn ich auf ein Observable zugreife.

Es ist auch etwas zerbrechlich. Wenn ich Punkte für den Zugriff() im Code versuche, kann ich nicht

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

, weil das zu Kontext der Punkte ändert(), um DOMWindow, statt Artikel.

Wenn ich die berechnete in create() in das Mapping, konnte ich den Kontext erfassen, aber dann gibt es eine Kopie der Methode für jede Objektinstanz.

Ich habe Michael Bests Google Groups Post gefunden (http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13). Der Prototyp gibt ein neues berechnetes Observable auf "activate" zurück. Ich habe nicht herausgefunden, was "aktivieren" (vielleicht Objs?) Genannt wird, aber ich vermute, dass es immer noch einmal pro Objekt passiert, und ich habe keine Ahnung, welchen Umfang "dieses" bekommen wird.

An diesem Punkt glaube ich, dass ich nicht mehr in den veröffentlichten Dokumenten bin, aber ich arbeite immer noch daran, zu entschlüsseln, was von der Quelle geschieht.

Antwort

7

Sie erwähnen, dass Sie keine Instanz der Funktion ko.computed für jede Instanz Ihrer JavaScript-Klasse haben möchten, das wird jedoch nicht wirklich funktionieren mit der Funktionalität von ko. Wenn Sie ko.computed oder ko.observable verwenden, erstellen sie spezifische Speicherzeiger für private Variablen innerhalb der Klasse, die normalerweise nicht für Klasseninstanzen freigegeben werden sollen (obwohl dies in seltenen Fällen möglich ist).

ich etwas wie folgt aus:

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

dann für den berechneten Observablen (die auf jeder Instanz Ihrer MyInheritedClass Instanz Funktionen sein) Ich erkläre ihnen nur in einem extender etwa so:

MyInheritedClass.prototype.extend(function(){ 

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

Bei Ihrem Beispiel und die Base Klasse oben definiert sind, können Sie leicht tun:

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

Dann haben alle Instanzen Ihrer Item Klasse eine Points Eigenschaft, und es wird die ko.computed Logik pro Instanz korrekt behandeln.

+0

Danke. Ich denke, dass der Zweck von .Extend() gerade geklickt hat. Dies löst meine Scoping/Doppelfunktion Aufrufprobleme schön, und ich werde dein Wort nehmen, dass Knockout mich nicht das tatsächlich errechnete Observable auf dem Prototyp behalten lässt. –

+0

Eric, könnten Sie eine mehr "Zucker" Version zur Verfügung stellen? So etwas wie childClass = BaseClass.extend (function() {/*...*/}), das bereits die Konfiguration der Prototypkette durchführt? Es manuell für jede Klasseninstanz zu tun ist ein bisschen seltsam ... – fbuchinger

+0

Ich sehe keinen Grund, warum Sie nicht eine statische Funktion 'erweitern' auf den geerbten Konstruktor setzen könnte, solange es im Grunde genommen: ' SubClass.extend = Funktion (Extender) {SubClass.prototype.extend (Extender); }; ' – 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); 
}; 
Verwandte Themen