I recently updated my angular.d.ts file in my typescript project. I am now getting a typescript compile 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 get this compiler error: Type ng.DirectiveFactory requires a call signature, but type "MyDirective" lacks ones.
This is how my directive looks right now (which used to work fine with older 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 this is where I register the MyDirective class with angular:
angular.module("MyModule", [])
.directive('myDirective', MyDirective);
The above compiler error makes perfect sense, but how do I implement the (...args: any[]): IDirective signature) ?
Thanks in advance
I know this is an old question, but I came across this problem as well and thought I would 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 allows you to use the full benefits of a TypeScript class.
UPDATE If you want to use this approach to simplify your link function using private class functions or fields, you will need to define your link function slightly 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 link method is here declared as a fat arrow function rather than a class function)
This is because when Angular wires this up, it does so in a way that doesn't preserve the this
reference for the class. By defining it with a fat arrow function, the compiled JavaScript will define the function in a way that will preserve the this
reference. Otherwise you will get lots of errors trying to run the code.
The old signature of directive()
used to be...
directive(name: string, directiveFactory: Function): IModule;
It is legal for a class to be used as a Function
. But this commit changed the signature to:
directive(name: string, directiveFactory: IDirectiveFactory): IModule;
IDirectiveFactory
is a function that returns an IDirective
, so directive()
no longer accepts a class for the directiveFactory
argument. 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);
Joe Skeen supplied an answer which was perfectly in line of what I was going for.
There is another guy Aaron Holmes worked out a similar solution to his, which includes some improvements to handling link.
http://blog.aaronholmes.net/writing-angularjs-directives-as-typescript-classes/#comment-2111298002
As opposed to Joe's answer above, instead of referencing ng.IScope directly, if you use an interface, you can define your own scope variables in the TypeScript fashion (intellisense!)
Also moving the logic for link to the constructor seems like a better way of doing it.
link = (scope: ng.IScope
, element: ng.IAugmentedJQuery
, attributes: IAttributes
, controller: any
, transclude: ng.ITranscludeFunction) => {
...
}
to:
link = (scope: IMyScope
, element: ng.IAugmentedJQuery
, attributes: IAttributes
, controller: any
, transclude: ng.ITranscludeFunction) => {
...
}
Here is 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 pitfall: You need to use a colon ":" instead of a "=" when declaring link if you're moving the link logic to the constructor as above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With