Add directives from directive in AngularJS

2018-12-31 10:00发布

I'm trying to build a directive that takes care of adding more directives to the element it is declared on. For example, I want to build a directive that takes care of adding datepicker, datepicker-language and ng-required="true".

If I try to add those attributes and then use $compile I obviously generate an infinite loop, so I am checking if I have already added the needed attributes:

angular.module('app')
  .directive('superDirective', function ($compile, $injector) {
    return {
      restrict: 'A',
      replace: true,
      link: function compile(scope, element, attrs) {
        if (element.attr('datepicker')) { // check
          return;
        }
        element.attr('datepicker', 'someValue');
        element.attr('datepicker-language', 'en');
        // some more
        $compile(element)(scope);
      }
    };
  });

Of course, if I don't $compile the element, the attributes will be set but the directive won't be bootstrapped.

Is this approach correct or am I doing it wrong? Is there a better way to achieve the same behavior?

UDPATE: given the fact that $compile is the only way to achieve this, is there a way to skip the first compilation pass (the element may contain several children)? Maybe by setting terminal:true?

UPDATE 2: I have tried putting the directive into a select element and, as expected, the compilation runs twice, which means there is twice the number of expected options.

7条回答
闭嘴吧你
2楼-- · 2018-12-31 10:47

I wanted to add my solution since the accepted one didn't quite work for me.

I needed to add a directive but also keep mine on the element.

In this example I am adding a simple ng-style directive to the element. To prevent infinite compile loops and allowing me to keep my directive I added a check to see if what I added was present before recompiling the element.

angular.module('some.directive', [])
.directive('someDirective', ['$compile',function($compile){
    return {
        priority: 1001,
        controller: ['$scope', '$element', '$attrs', '$transclude' ,function($scope, $element, $attrs, $transclude) {

            // controller code here

        }],
        compile: function(element, attributes){
            var compile = false;

            //check to see if the target directive was already added
            if(!element.attr('ng-style')){
                //add the target directive
                element.attr('ng-style', "{'width':'200px'}");
                compile = true;
            }
            return {
                pre: function preLink(scope, iElement, iAttrs, controller) {  },
                post: function postLink(scope, iElement, iAttrs, controller) {
                    if(compile){
                        $compile(iElement)(scope);
                    }
                }
            };
        }
    };
}]);
查看更多
登录 后发表回答