Using ES6 Classes as Angular 1.x directives

后端 未结 10 812
南旧
南旧 2020-12-04 07:57

I\'m doing a small project to play around the goody bag the ES6 is bringing, I\'m trying to set register a class as an angular directive, but I\'m running into this error \"

相关标签:
10条回答
  • 2020-12-04 08:08
    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);
    
    0 讨论(0)
  • 2020-12-04 08:11

    I ran into this problem just now and I saw this topic. Tried some methods provided in discussion, I finally solved this problem in a very simple way:

    export default function archiveTreeDirective() {
        'ngInject';
    
        return {
            restrict: 'E',
            scope: {
                selectedNodes: "="
            },
            templateUrl: 'app/components/directives/archiveTree/archiveTree.html',
            controller: ArchiveTreeController,
            controllerAs: 'vm',
            bindToController: true
        };
    }
    
    class ArchiveTreeController {
        constructor() {
            'ngInject';
            ...
        }
        ...
    }
    

    I directly use function as the .directive('directiveName',factory) argument, and export it, later import it in module declaration. But I missed the "default" statement when exporting, so I got an error. After I add "default" key word, everything works!

    I find this method also works in my route configs (also in a function way).

    ============ Hope you can understand my poor English :)

    0 讨论(0)
  • 2020-12-04 08:12

    In my project I use a syntax sugar for injections. And ES6 makes it pretty simple to use injectable factories for directives avoiding too much duplicate code. This code allows injection inheritance, uses annotated injections and so on. Check this:

    First step

    Declare base class for all angular controllers\directives\services - InjectableClient. Its main task - set all injected params as properties for 'this'. This behavior can be overridden, see examples below.

    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);
            }
        };
    }
    

    For example, if we call SomeClassInheritedFromInjectableClient.inject('$scope'), in directive or controller we will use it as 'this.$scope'

    Second step

    Declare the base class for directive with static method "factory()", which binds $injected property of directive class to factory function. And also "compile()" method, which binds the context of link function to the directive itself. Its allows to use our injected values inside the link function as 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;
        }
    }
    

    Third step

    Now we can declare as much directive classes as possible. With inheritance. And we can set up injections in simple way with spread arrays (just dont forget call super method). See examples:

    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);
        }
    }    
    

    The last step

    Now register directives with angular in simple way:

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

    Now test the code:

    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));
    

    This will return:

    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"}
    
    0 讨论(0)
  • 2020-12-04 08:16

    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.

    0 讨论(0)
  • 2020-12-04 08:18

    I faced the same problem. First time I tried to solve problem via ES6 classes but I have problem with $inject my dependencies. After I realized what angular have couple styles of writing code and I tried. At all I used John Papa styles and I got this works code in my rails app with 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);
    

    I know that I can do little bit improvements with functions and variables in more ES6 style. I hope it helps.

    0 讨论(0)
  • 2020-12-04 08:20

    A simpler, cleaner and more readable solution

    0 讨论(0)
提交回复
热议问题