How to implement ng.IDirectiveFactory in TypeScript
I recently updated the angular.d.ts file in my typescript project. Now I am getting typescript compilation error in my Directive definitions. I noticed the following in the updated angular.d.ts file:
interface IDirectiveFactory {
(...args: any[]): IDirective;
}
I am trying to figure out how to implement this interface.
I am getting this compiler error: The ng.DirectiveFactory type requires a call signature, but the "MyDirective" type is missing.
This is what my directive looks like right now (which worked fine with the old angular.d.ts file):
class MyDirective{
constructor() {
var directive: ng.IDirective = <ng.IDirective>{};
directive.priority = 0;
directive.restrict = "E";
directive.scope = {...};
directive.template = '<div></div>';
directive.link = {...}
return directive;
}
}
And here I am registering the MyDirective class with angular:
angular.module("MyModule", [])
.directive('myDirective', MyDirective);
The above compiler error makes sense, but how can I implement (... args: any []): signature with id)?
Thank you in advance
source to share
The old signature directive()
used to be ...
directive(name: string, directiveFactory: Function): IModule;
It is legal for a class to be used as Function
. But this commit changed the signature to:
directive(name: string, directiveFactory: IDirectiveFactory): IModule;
IDirectiveFactory
is a function that returns IDirective
, so it directive()
no longer takes a class for an argument directiveFactory
. Change it to ...
function MyDirective () : ng.IDirective {
var directive: ng.IDirective = <ng.IDirective>{};
directive.priority = 0;
directive.restrict = "E";
directive.scope = {};
directive.template = '<div></div>';
return directive;
}
angular.module("MyModule", [])
.directive('myDirective', MyDirective);
source to share
I know this is an old question, but I ran into this problem and thought I'd share my solution:
class MyDirective implements ng.IDirective {
priority = 0;
restrict = 'E';
scope = {...};
template = '<div></div>';
link(scope: ng.IScope
, element: ng.IAugmentedJQuery
, attributes: IAttributes
, controller: any
, transclude: ng.ITranscludeFunction) {
...
}
}
angular.module("MyModule", [])
.directive('myDirective', () => new MyDirective());
I like this solution because it takes full advantage of the TypeScript class.
UPDATE If you want to use this approach to simplify your link function using functions or private class fields, you will need to define your link function a little differently:
class MyDirective implements ng.IDirective {
priority = 0;
restrict = 'E';
scope = {...};
template = '<div></div>';
link = (scope: ng.IScope
, element: ng.IAugmentedJQuery
, attributes: IAttributes
, controller: any
, transclude: ng.ITranscludeFunction) => {
...
}
}
angular.module("MyModule", [])
.directive('myDirective', () => new MyDirective());
(note that the reference method here is declared as a fat arrow function, not a class function)
This is because when Angular is laying it out, it does it in such a way as not to keep the reference this
for the class. By defining it with the bold arrow function, the compiled JavaScript will define the function to preserve the link this
. Otherwise, you will get a lot of errors trying to run the code.
source to share
Joe Skeen provided an answer that was exactly in line with what I was about to do.
There is another guy that Aaron Holmes designed a similar solution for him that includes some improvements in the handling of the link.
http://blog.aaronholmes.net/writing-angularjs-directives-as-typescript-classes/#comment-2111298002
Contrary to Joe's answer above, instead of directly linking to ng.IScope if you are using the interface, you can define your own scope variables in the TypeScript unit (intellisense!)
Also moving the logic to reference the constructor seems like the best way to do it.
link = (scope: ng.IScope , element: ng.IAugmentedJQuery , attributes: IAttributes , controller: any , transclude: ng.ITranscludeFunction) => { ... }
in
link = (scope: IMyScope , element: ng.IAugmentedJQuery , attributes: IAttributes , controller: any , transclude: ng.ITranscludeFunction) => { ... }
Here's Aaron's complete example:
module MyModule.Directives
{
export interface IMyScope: ng.IScope
{
name: string;
}
export class MyDirective
{
public link: (scope: IMyScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => void;
public template = '<div>{{name}}</div>';
public scope = {};
constructor()
{
MyDirective.prototype.link = (scope: IMyScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) =>
{
scope.name = 'Aaron';
};
}
public static Factory()
{
var directive = () =>
{
return new MyDirective();
};
directive['$inject'] = [''];
return directive;
}
}
}
One mistake: you need to use colon ":" instead of "=" when declaring a link, if you are moving the linking logic to the constructor as above.
source to share