angularjs - Hur man skapar ett nytt objekt i typescript som har angular-injecterade parametrar


Jag har en TypeScript-klass, i vars konstruktor jag har ett vanligt och ett angular-injicerat argument:

export class MyClass {
    private translation:string;

    public static $inject = ['$filter'];
    constructor(name:string, $filter: ng.IFilterService) {
        this.translation = filter('translate')('code').toString();
    }
}

Om jag nu vill skapa ett objekt, hur kan jag göra det?

new MyClass('myname'); //won't compile because there are too few parameters
new MyClass('myname', filter); //makes no sense since I want to inject it

Även om jag skrev $filter? kommer det inte att fungera eftersom det inte kommer att känna igen omfattningen och det kommer att vara odefinierat.

Så, hur kan jag få detta att fungera?

Min strategi

Säg att jag är i en annan klass, där jag vill skapa ett objekt av MyClass. Följande kod kommer att fungera men jag gillar inte idén att behöva injicera $filter i den här klassen också, eftersom den inte behöver det.

export class ClassUsingTheOtherClass {
    private filter:ng.IFilterService;

    public static $inject = ['$filter'];
    constructor($filter: ng.IFilterService) {
        this.filter = $filter;
    }

    doThings() {
        var clazz = new MyClass('myName', this.filter);
    }
}

Jag skulle hellre kalla något i stil med detta var clazz=ny MyClass('myName'); och att $filter automatiskt injiceras som beroende i MyClass. Är detta alls möjligt?

Svar #1

Jag tror att du missar något om DI (injektion). Poängen är att inte skapa några beroenden manuellt. Istället ska man alltid lösa dem, och med Angular görs detta automatiskt i konstruktorn. De enda undantagen bör vara klasser som har 0 Angular-beroenden och har begränsat till inget beteende, som en ren entity/data-klass.

Dessutom ger Typescript dig möjlighet att definiera gränssnitt (kontrakt). Du bör skapa dem och använda dem som referenspunkter istället för dina klasstyper direkt. Detta gör att du kan ändra beteende eller till och med definitioner (ger lös koppling). Detta gör också enhetstestning enklare.

Ditt exempel skulle kunna skrivas om så här:

export interface IMyService{
   doThing:()=>void;
}

// should be registered with angular with service name 'ngMyService'
export class MyClass implements IMyService {
    private translation:string;

    public static $inject = ['$filter'];
    constructor($filter: ng.IFilterService) {
        this.translation = $filter('translate')('code').toString();
    }

    doThing() : void {
        // something
    }
}

export class ClassUsingTheOtherClass {

    public static $inject = ['ngMyService'];
    constructor(private myService: IMyService) {
    }

    doThings() {
        this.myService.doThing();
    }
}

Registrera nu MyClass med Angular med namnet ngMyService och registrera även ClassUsingTheOtherClass med Angular. Var du registrerar dem beror på deras användning (controller, service, factory, filter, etc).

När Angular skapar en instans av ClassUsingTheOtherClass så injiceras automatiskt MyClass i konstruktorn. MyClass kommer i sin tur automatiskt att få $filter injicerad i konstruktorn.

Till slut ser jag ingen mening med strängnamnsparametern i MyClass så jag tog bort den. Du kommer aldrig kunna injicera detta eftersom du inte kan registrera en sträng med vinkel som ska injiceras. Du kan antingen hårdkoda namnet i själva typen ELLER ange en namnegenskap/fält som kan ställas in av användaren av MyClass (lägga till fältet i gränssnittet IMyService), men detta bryter enligt min mening mot regeln om hög kohesion.

Svar #2

export class MyClass {

    public static $inject = ['$scope'];
    constructor(name:string, $scope?: ng.IScope) {
        //do things...                
    }
}

Genom att modifiera din klass så att det finns ett frågetecken bakom $scope, berättar du för kompilatorn att $scope är en valfri parameter.

Nu kan du enkelt köra new MyClass('myname');.

Relaterade artiklar