Using ES6 Classes as Angular 1.x directives
From my point of view, there is no need to use external libraries like register.js, because you can create directive as a ES6 class in this way:
class MessagesDirective { constructor() { this.restrict = 'E' this.templateUrl = 'messages.html' this.scope = {} } controller($scope, $state, MessagesService) { $scope.state = $state; $scope.service = MessagesService; } link(scope, element, attrs) { console.log('state', scope.state) console.log('service', scope.service) }}angular.module('messages').directive('messagesWidget', () => new MessagesDirective)
Using directive controller allows you to inject dependencies, even without additional declaration (ex. MessagesDirective.$inject = ['$scope', '$state', 'MessagesService']
), so you can use services in link function via scope if you need.
As mentioned in a comment, the module.directive()
method expects a factory function rather than a constructor.
The most simple way would be to wrap your class in a function that returns an instance:
angular.module('app') .directive('dateBlock', () => new DateBlock());
However, this will only work in the most limited sense - it does not allow for dependency injection and the compile
and link
functions of your directive (if defined) will not work as expected.
In fact, this is a problem I have looked into quite extensively and it turned out to be fairly tricky to solve (for me at least).
I wrote an extensive article covering my solution, but as far as you are concerned I can point you to the discussion of the two main issues that need to be resolved:
Dynamically converting a class definition into an angular-compatible factory function
Allowing a directive's
link
andcompile
functions to be defined as class methods
The full solution involves too much code to paste here, I think, but I have put together a working demo project which allows you to define a directive as an ES6 class like this:
class MyDirective { /*@ngInject*/ constructor($interval) { this.template = '<div>I\'m a directive!</div>'; this.restrict = 'E'; this.scope = {} // etc. for the usual config options // allows us to use the injected dependencies // elsewhere in the directive (e.g. compile or link function) this.$interval = $interval; } // optional compile function compile(tElement) { tElement.css('position', 'absolute'); } // optional link function link(scope, element) { this.$interval(() => this.move(element), 1000); } move(element) { element.css('left', (Math.random() * 500) + 'px'); element.css('top', (Math.random() * 500) + 'px'); }}// `register` is a helper method that hides all the complex magic that is needed to make this work.register('app').directive('myDirective', MyDirective);
Check out the demo repo here and here is the code behind register.directive()
@Michael is right on the money:
the module.directive() method expects a factory function
However I solved it using another technique, a little cleaner I suppose, It works fine for me, it's not perfect though...I defined a static method that returns a the factory expected by module()
class VineDirective { constructor($q) { this.restrict = 'AE'; this.$q = $q; } link(scope, element, attributes) { console.log("directive link"); } static directiveFactory($q){ VineDirective.instance = new VineDirective($q); return VineDirective.instance; }}VineDirective.directiveFactory.$inject = ['$q'];export { VineDirective }
And in my app I do:
angular.module('vineyard',[]).directive('vineScroller', VineDirective.directiveFactory)
I believe there's no other way to use classes + directives that going through hacks like this at this point, just pick the easy one ;-)