iberbeu
iberbeu

Reputation: 16205

How to create new object in typescript having angular-injected parameters

I have a typescript class, in which constructor I have a normal and an angular-injected argument:

export class MyClass {
    private translation:string;

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

If I now want to create an object, how can I do it?

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

Even If I wrote $filter? it won't work because it won't recognize the scope and it will be undefined.

So, How can I get this to work?

My Approach

Let's say I am in another class, in which I want to create an object of MyClass. The following code will work but I don't like the idea of having to inject $filter in this class too, since it doesn't need it.

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

I'd rather call something like this var clazz = new MyClass('myName'); and having $filter automatically injected as dependency in MyClass. Is this possible at all?

Upvotes: 4

Views: 6273

Answers (2)

Igor
Igor

Reputation: 62213

I think you are missing something about DI (injection). The point is to not create any dependencies manually. Instead always resolve them and with Angular this is done in the constructor automatically. The only exceptions should be with classes that have 0 angular dependencies and have limited to no behavior like a pure entity/data class.

Also Typescript affords you the ability to define interfaces (contracts). You should create them and use these as reference points instead of your class types directly. This will allow you to change behavior or even definitions (provides loose coupling). This also makes unit testing easier.

Your example could be rewriten like so:

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

Now register MyClass with angular with the name ngMyService and register ClassUsingTheOtherClass with angular as well. Where you register them depends on their use (controller, service, factory, filter, etc).

When angular creates an instances of ClassUsingTheOtherClass then it automatically has MyClass injected into the constructor. MyClass in turn will automatically have $filter injected into the constructor.

Finally I see no purpose to the string name parameter in MyClass so I removed it. You will never be able to inject this because you cannot register a string with angular to be injected. You could either hard code the name in the type itself OR provide a name property / field that could be set by the user of MyClass (add the field to the interface IMyService) but this breaks the high cohesion rule in my opinion.

Upvotes: 5

Pjetr
Pjetr

Reputation: 1382

export class MyClass {

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

By modding your class so there is a question mark behind $scope, you tell the compiler that the $scope is an optional parameter.

Now you can simply run new MyClass('myname');.

Upvotes: -2

Related Questions