I'm trying to create an AngularJS directive using TypeScript. My directive requires 'ngModel' and I'm also using a custom service injected in my directive. My main problem is that my service can't be used inside my link function.
Here is an example of what I'm trying to achieve:
module app.directives {
export var directiveName: string = "theDirective";
angular.module("myApp").directive(directiveName,
(myFactory: app.services.MyFactory) =>
{
return new MyDirective(myFactory);
});
export interface IMyDirectiveScope extends ng.IScope {
ngModel: ng.INgModelController;
}
export class MyDirective implements ng.IDirective {
restrict = "A";
require = "ngModel";
scope = {
ngModel:'='
}
constructor(private myFactory: app.services.MyFactory) {
}
link(scope: IMyDirectiveScope , elem: JQuery, attributes: ng.IAttributes, ngModel: ng.INgModelController) {
//this is window here
elem.bind('blur', (evt: JQueryEventObject) => {
//keyword this is also window here, so yeah bummer indeed
validate();
});
function validate() {
//I need to use my factory here, but I can seem to get it.
//this is always window and I'm kinda stuck here
}
}
}
}
I can't seem to find some more advanced stuff on this topic. All the examples don't I find don't seem to uses services or a complex link function. Please answer this question with some sort of example. It's trickery that you think.
Update: The fact that 'this' inside my link function is window and not 'MyDirective' doesn't make much sense to me. Any ideas why that would be?
Using classes and inherit from ng.IDirective is the way to go with TypeScript.
TypeScript includes support for the fat arrow function () =>
from EcmaScript 6.
It's a shorthand syntax that also changes the way the this
keyword works:
class MyDirective implements ng.IDirective {
restrict = 'A';
require = 'ngModel';
scope = {
ngModel: '='
}
constructor(private myFactory: app.services.MyFactory) {
}
link = (scope: IMyDirectiveScope, elem: JQuery, attributes: ng.IAttributes, ngModel: ng.INgModelController) => {
console.log(this); // this points to MyDirective instance instead of Window
elem.bind('blur', (evt: JQueryEventObject) => {
console.log(this); // this points to MyDirective instance instead of Window
this.validate();
});
}
validate() {
console.log(this); // this points to MyDirective instance instead of Window
}
static factory(): ng.IDirectiveFactory {
var directive = (myFactory: app.services.MyFactory) => new MyDirective(myFactory);
directive.$inject = ['myFactory'];
return directive;
}
}
app.directive('mydirective', MyDirective.factory());
You can also rely on the old fashion var self = this;
pattern:
class MyDirective implements ng.IDirective {
restrict = 'A';
require = 'ngModel';
scope = {
ngModel: '='
}
constructor(private myFactory: app.services.MyFactory) {
}
link = (scope: IMyDirectiveScope, elem: JQuery, attributes: ng.IAttributes, ngModel: ng.INgModelController) => {
console.log(this); // this points to MyDirective instance instead of Window
var self = this;
function validate() {
console.log(self); // self points to MyDirective instance
}
elem.bind('blur', function(evt: JQueryEventObject) {
console.log(self); // self points to MyDirective instance
validate();
});
}
}
Related answer: https://stackoverflow.com/a/29223535/990356
Classes work great for controllers and directive controllers but I don't think I'd use one for the whole directive. But if you want to you'd probably have to do something like this:
export class MyDirective implements ng.IDirective {
public link;
restrict = "A";
require = "ngModel";
scope = {
ngModel:'='
}
constructor(private myFactory: app.services.MyFactory) {
this.link = this.unboundLink.bind(this);
}
unboundLink(scope: IMyDirectiveScope , elem: JQuery, attributes: ng.IAttributes, ngModel: ng.INgModelController) {
//Now you should be able to access myFactory
this.myFactory.doSomething();
elem.bind('blur', (evt: JQueryEventObject) => {
//keyword this is also window here, so yeah bummer indeed
validate();
});
function validate() {
//I need to use my factory here, but I can seem to get it.
//this is always window and I'm kinda stuck here
}
}
}
EDIT: Without a class you could do something like this:
angular.module("myApp").directive("theDirective",
function(myFactory: app.services.MyFactory) {
return {
restrict: 'A',
require: 'ngModel',
scope: {'ngModel': '='},
link: function(scope: IMyDirectiveScope , elem: JQuery, attributes: ng.IAttributes, ngModel: ng.INgModelController) {
//You can access myFactory like this.
myFactory.doSomething();
}
}
}
);
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