使用ES6类作为Angular 1.x指令

我正在做一个小项目,围绕着ES6带来的好东西,我试图设置一个类作为一个angular度指令,但我遇到了这个错误“TypeError:不能调用一个类作为一个函数“,但从例子中,我发现他们只是写作课,并注册作为一个指令的angular度。 这是我的指令。

class dateBlock { constructor () { this.template = '/app/dateblock/dateblock.html'; this.restrict = 'AE'; this.scope = {}; } }; export default dateBlock 

和我的索引,我将其导入,然后声明它。

 import calendarController from './calendar/calendar.js' import dateBlock from './dateblock/dateblock.js' function setup($stateProvider) { $stateProvider .state('base', { url: '', controller: calendarController, templateUrl: '/app/calendar/calendar.html' }); }; setup.$inject = ['$stateProvider'] var app = angular.module('calApp',['ngAnimate','ui.router','hmTouchEvents', 'templates']) .config(setup) .controller('calendarController', calendarController) .directive('dateBlock', dateBlock) 

如果我错过了一些关键的步骤,我很乐意听到它。 另外一个问题是,将所有的应用程序组件导入到索引并将其注册到那里或导出应用程序并在组件内导入和注册是更清洁的?

从我的angular度来看,不需要使用像register.js这样的外部库,因为您可以通过这种方式创build指令作为ES6类:

 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) 

使用指令控制器允许你注入依赖关系,即使没有额外的声明(例如MessagesDirective.$inject = ['$scope', '$state', 'MessagesService'] ),所以你可以通过范围在链接函数中使用服务。需要。

正如在注释中提到的, module.directive()方法需要一个工厂函数而不是一个构造函数。

最简单的方法是将你的类包装在一个返回实例的函数中:

 angular.module('app') .directive('dateBlock', () => new DateBlock()); 

但是,这只能在最有限的意义上工作 – 它不允许dependency injection,而且你的指令的compilelinkfunction(如果定义的话)不能按预期工作。

事实上,这是一个我已经深入研究的问题,结果相当棘手(至less对我来说)。

我写了一篇关于我的解决scheme的广泛的文章,但就你而言,我可以指出你讨论的两个需要解决的主要问题:

  1. 将类定义dynamic转换为angular度兼容的工厂函数

  2. 允许指令的linkcompile函数被定义为类方法

完整的解决scheme涉及到太多的代码粘贴在这里,我想,但我已经把一个工作演示项目,它允许你定义一个ES6类的指令是这样的:

 class MyDirective { /*@ngInject*/ constructor($interval) { this.template = '<div>I\'ma directive!</div>'; this.restrict = 'E'; this.scope = {} // etc. for the usual config options // allows us to use the injected dependencies // elsewhere in the directive (eg 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); 

看看这里的演示回购 , 这里是register.directive()后面的代码

@迈克尔是对的钱:

module.directive()方法需要一个工厂函数

然而,我解决了它使用另一种技术,我认为一个小清洁剂,它对我来说工作得很好,虽然它不是完美的…我定义了一个静态方法返回一个工厂所期望的模块()

 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 } 

而在我的应用程序,我做的:

 angular.module('vineyard',[]).directive('vineScroller', VineDirective.directiveFactory) 

我相信没有其他的方式来使用类似这样的黑客的类+指令,只需select一个简单的;-)

正如@迈克尔·布罗姆利指出,在探索es6-classes-in-angularjs-1-x# _section 工厂 ,@ bmaggi你不能在link函数中获得$qthis link将是undefinednull

当Angular调用链接函数时,它不再处于类实例的上下文中,因此这个。$ interval将是未定义的

因此,在指令中使用controller函数,并注入依赖关系或任何你想要在link函数中引用的东西。 这不是比前两个高度投票的答案更有生产力和逻辑吗?

 class ClipBoardText { constructor() { console.log('constructor'); this.restrict = 'A'; this.controller = ClipBoardTextController; } link(scope, element, attr, ctr) { console.log('ctr', ctr); console.log('ZeroClipboard in link', ctr.ZeroClipboard); console.log('q in link', ctr.q); } static directiveFactory() { return new ClipBoardText(); } } // do not $inject like this // ClipBoardText.$inject = ['$q']; class ClipBoardTextController { constructor(q) { this.q = q; this.ZeroClipboard = 'zeroclipboard'; } } ClipBoardTextController.$inject = ['$q']; export default ClipBoardText.directiveFactory; 

在我的项目中,我使用注射糖的语法。 ES6使得使用可注入工厂的指令非常简单,避免了太多重复的代码。 这段代码允许注入inheritance,使用注释注入等等。 检查这个:

第一步

声明所有angular度控制器\指令\服务的基类 – InjectableClient。 其主要任务 – 将所有注入的参数设置为“this”的属性 。 这个行为可以被覆盖,见下面的例子。

 class InjectionClient { constructor(...injected) { /* As we can append injections in descendants we have to process only injections passed directly to current constructor */ var injectLength = this.constructor.$inject.length; var injectedLength = injected.length; var startIndex = injectLength - injectedLength; for (var i = startIndex; i < injectLength; i++) { var injectName = this.constructor.$inject[i]; var inject = injected[i - startIndex]; this[injectName] = inject; } } static inject(...injected) { if (!this.$inject) { this.$inject = injected; } else { this.$inject = injected.concat(this.$inject); } }; } 

例如,如果我们在指令或控制器中调用SomeClassInheritedFromInjectableClient.inject('$ scope'),我们将使用它作为'this。$ scope'

第二步

使用静态方法“factory()”声明指令的基类,该方法将指令类的$ inject属性绑定到工厂函数。 还有“compile()”方法,它将链接函数的上下文绑定到指令本身。 它允许在链接函数内使用我们的注入值this.myInjectedService。

 class Directive extends InjectionClient { compile() { return this.link.bind(this); } static factory() { var factoryFunc = (...injected) => { return new this(...injected); } factoryFunc.$inject = this.$inject; return factoryFunc; } } 

第三步

现在我们可以声明尽可能多的指令类。 inheritance。 我们可以用简单的方式设置注射数组(不要忘记调用超级方法)。 看例子:

 class DirectiveFirst extends Directive { } DirectiveFirst.inject('injA', 'injB', 'injC'); class DirectiveSecond extends DirectiveFirst { constructor(injD, ...injected) { super(...injected); this.otherInjectedProperty = injD; } } // See appended injection does not hurt the ancestor class DirectiveSecond.inject('injD'); class DirectiveThird extends DirectiveSecond { constructor(...injected) { // Do not forget call the super method in overridden constructors super(...injected); } } 

最后一步

现在以简单的方式用angular来注册指令:

 angular.directive('directiveFirst', DirectiveFirst.factory()); angular.directive('directiveSecond', DirectiveSecond.factory()); angular.directive('directiveThird', DirectiveThird.factory()); 

现在testing代码:

 var factoryFirst = DirectiveFirst.factory(); var factorySec = DirectiveSecond.factory(); var factoryThird = DirectiveThird.factory(); var directive = factoryFirst('A', 'B', 'C'); console.log(directive.constructor.name + ' ' + JSON.stringify(directive)); directive = factorySec('D', 'A', 'B', 'C'); console.log(directive.constructor.name + ' ' + JSON.stringify(directive)); directive = factoryThird('D', 'A', 'B', 'C'); console.log(directive.constructor.name + ' ' + JSON.stringify(directive)); 

这将返回:

 DirectiveFirst {"injA":"A","injB":"B","injC":"C"} DirectiveSecond {"injA":"A","injB":"B","injC":"C","otherInjectedProperty":"D"} DirectiveThird {"injA":"A","injB":"B","injC":"C","otherInjectedProperty":"D"} 

我的解决scheme

 class myDirective { constructor( $timeout, $http ) { this.restrict = 'E'; this.scope = {}; this.$timeout = $timeout; this.$http = $http; } link() { console.log('link myDirective'); } static create() { return new myDirective(...arguments); } } myDirective.create.$inject = ['$timeout', '$http']; export { myDirective } 

并在主应用程序文件中

 app.directive('myDirective', myDirective.create) 

我有一个类似的问题。 但在我的情况下,当我部署到生产时,它工作并失败。 由于生产有最新版本的6to5,所以失败了。 这可以通过使用npm shrinkwrap来防止。 根据最新的ES6规范,你不能使用这样的类。 https://github.com/babel/babel/issues/700

我面临同样的问题。 第一次我试图通过ES6类来解决问题,但我有问题注入我的依赖。 当我意识到什么angular度有几种写代码的风格,我试过了。 在所有我使用约翰爸爸的风格,我得到了这个工程代码在我的轨道应用与ES6:

 ((angular) => { 'use strict'; var Flash = ($timeout) => { return { restrict: 'E', scope: { messages: '=messages' }, template: (() => { return "<div class='alert flash-{{ message[0] }}' ng-repeat = 'message in messages'>" + "<div class= 'close' ng-click = 'closeMessage($index)' data-dismiss = 'alert' > × </div>" + "<span class= 'message' >{{ message[1] }}</ span>" + "</ div>"; }), link: (scope) => { scope.closeMessage = (index) => { scope.messages.splice(index, 1) }; $timeout(() => { scope.messages = [] }, 5000); } } }; Flash.$inject = ['$timeout']; angular.module('Application').directive('ngFlash', Flash); })(window.angular); 

我知道我可以使用更多ES6风格的function和variables做一些改进。 我希望它有帮助。

 class ToggleShortcut{ constructor($timeout, authService, $compile, $state){ var initDomEvents = function ($element, $scope) { var shortcut_dropdown = $('#shortcut'); $compile(shortcut_dropdown)($scope); $scope.goToShortCutItem = function(state, params){ var p = params || null; if(state === 'app.contacts.view'){ var authProfile = authService.profile; if(authProfile){ p = { id:authProfile.user_metadata.contact_id }; } } $state.go(state, p); window.setTimeout(shortcut_buttons_hide, 300); }; $element.on('click', function () { if (shortcut_dropdown.is(":visible")) { shortcut_buttons_hide(); } else { shortcut_buttons_show(); } }); // SHORTCUT buttons goes away if mouse is clicked outside of the area $(document).mouseup(function (e) { if (shortcut_dropdown && !shortcut_dropdown.is(e.target) && shortcut_dropdown.has(e.target).length === 0) { shortcut_buttons_hide(); } }); // SHORTCUT ANIMATE HIDE function shortcut_buttons_hide() { shortcut_dropdown.animate({ height: "hide" }, 300, "easeOutCirc"); $('body').removeClass('shortcut-on'); } // SHORTCUT ANIMATE SHOW function shortcut_buttons_show() { shortcut_dropdown.animate({ height: "show" }, 200, "easeOutCirc"); $('body').addClass('shortcut-on'); } }; var link = function($scope, $element){ $timeout(function(){ initDomEvents($element, $scope); }); }; this.restrict = 'EA'; this.link = link; } } toggleShortcut.$inject = ['$timeout', 'authService', '$compile', '$state']; function toggleShortcut($timeout, authService, $compile, $state){ return new ToggleShortcut($timeout, authService, $compile, $state); } angular.module('app.layout').directive('toggleShortcut', toggleShortcut);