Reputation: 16205
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
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
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