Using ES6 Classes as Angular 1.x directives

后端 未结 10 863
南旧
南旧 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: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"}
    

提交回复
热议问题