2015-11-25 5 views
8

Ich baue eine einfache Zuordnung zwischen Frontend/Backend-Datenstruktur. Um das zu tun, dass ich ein Dekorateur erstellt haben, die wie folgt aussieht: Zuweisen von Eigenschaften zu Nicht-Prototyp mit Dekoratoren

function ApiField(
    apiKey: string, 
    setFn: (any) => any = (ret) => ret, 
    getFn: (any) => any = (ret) => ret 
) { 
    return function (target: AbstractModel, propertyKey: string) { 
     target.apiFieldsBag = target.apiFieldsBag || {}; 
     _.assign(
      target.apiFieldsBag, 
      { 
       [propertyKey]: { 
        apiKey: apiKey, 
        setFn: setFn, 
        getFn: getFn 
       } 
      } 
     ); 
    }; 
} 

Und das ist, wie ich es verwenden:

class AbstractCar { 
    @ApiField('id') 
    public id: string = undefined; 
} 

class BMW extends AbstractCar { 
    @ApiField('cylinders') 
    public cylinderCount: number; 
} 

class VW extends AbstractCar { 
    @ApiField('yearCompanyFounded') 
    public yearEstablished: number; 
} 

Das Problem, das ich sehe, ist, dass statt das eigentliche Objekt an den Dekorateur geleitet wird es immer sein Prototyp:

__decorate([ 
    ApiField('yearCompanyFounded') 
], VW.prototype, "yearEstablished", void 0); 

was bedeutet, dass, wie ich Sachen in dem Dekorateur zum Beispiel am zuweisen, es immer mit dem Prototyp angebracht ist, die wiederum bedeutet, dass Eigenschaften, die ich möchte nur die VW Instanz definiert sind auch auf der Klasse verfügbar (in diesem Beispiel wäre dies). Dies macht es unmöglich, zwei Eigenschaften mit demselben Namen, aber unterschiedlichen API-Feldern in zwei verschiedenen Klassen zu haben.

Gibt es eine Möglichkeit, dieses Verhalten zu umgehen?

+0

ECMAScript 7 ... !!!! – Arjun

+0

Nebenbei, Sie brauchen nicht die 'target.apiFieldsBag =' vor '_.assign ('. Sie haben auch die Option, ES6 'Object.assign()' anstelle von '_.assign() zu verwenden ' –

+0

Yeah, dachte ich, als ich wieder über die Frage nachdachte.Auch Danke für die Köpfe auf jeden Fall :) –

Antwort

4

jetzt rechts, alle drei Klassen Eigenschaften hinzufügen zu das gleiche Objekt. Der Schlüssel, um dies zu beheben, ist Clone das Objekt auf target.data, so dass jede Klasse ein anderes Objekt verwendet, anstatt alle auf das gleiche Objekt beziehen.

Hier ist ein einfacheres Beispiel, die eine Art und Weise zeigt, dies zu tun:

function ApiField(str: string) { 
    return function (target: any, propertyKey: string) { 
     // I tested with Object.assign, but it should work with _.assign the same way 
     target.data = _.assign({}, target.data, { 
      [propertyKey]: str 
     }); 
    }; 
} 

class AbstractCar { 
    @ApiField("car") 
    public carID; 
} 

class BMW extends AbstractCar { 
    @ApiField("bmw") 
    public bmwID; 
} 

class VW extends AbstractCar { 
    @ApiField("vw") 
    public vwID; 
} 

AbstractCar.prototype.data; // Object {carID: "car"} 
BMW.prototype.data;   // Object {carID: "car", bmwID: "bmw"} 
VW.prototype.data;   // Object {carID: "car", vwID: "vw"} 
+0

Die Sache ist, ich möchte die BMW-Klasse die Daten von der AbstractCar –

+0

@justNik ah erben, ok. Ich habe meine Antwort mit etwas aktualisiert, das dafür geeignet ist. –

+0

Süß, danke! Lustig, wie so ein kompliziert aussehendes Ding auf solch ein gewöhnliches Javascript-Problem hinausläuft. Sprechen Sie darüber, dass Sie den Wald für die Bäume vermissen –

4

Das Problem ist, dass public innerhalb einer Klasse nicht Standard-JavaScript ist, ist es nur etwas, das TypeScript tut. Deshalb müssen Sie vorsichtig sein, denn alles, was Sie tun, kann in der Zukunft brechen.

Eine Möglichkeit Object.assign() verwenden Instanz Eigenschaften hinzufügen (IINM, apiFieldsBag sollte aus dem durch die Objektliteral zu this erstellte Objekt übertragen werden):

class AbstractCar { 
    constructor() { 
     Object.assign(this, { 
      @ApiField('id') 
      id: undefined, 
     }); 
    } 
} 
+0

Ich benutze eigentlich Typoskript, also 'Public' sollte in Ordnung sein :) Aber ich werde deine Lösung versuchen –

+1

@justNik Ich weiß, dass 'public' TypeScript ist. Das Problem mit 'public foo = x' ist, dass es nicht als eine tatsächliche Entität existiert (z. B. eine Eigenschaft), es wird zu einer Zuweisung im Konstruktor kompiliert. Daher ist nicht klar, was dekoriert werden soll, denn, ATM, können nur Klassen und Eigenschaften dekoriert werden. –

+0

@justNik Ich habe mir nur den Code angeschaut, mit dem ein dekoriertes 'public' kompiliert wird und TypeScript schmückt tatsächlich eine Eigenschaft des Prototyps. Das ist ein Bug, IMO (er sollte eine Eigenschaft von 'this' im Konstruktor schmücken) - vielleicht möchtest du einen Fehlerbericht einreichen. –

Verwandte Themen