从 AngularJS 中的指令添加指令 [英] Add directives from directive in AngularJS

查看:35
本文介绍了从 AngularJS 中的指令添加指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建一个指令,负责为声明它的元素添加更多指令.例如,我想构建一个指令,负责添加 datepickerdatepicker-languageng-required="true".

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".

如果我尝试添加这些属性然后使用 $compile 我显然会生成一个无限循环,所以我正在检查是否已经添加了所需的属性:

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

当然,如果我不$compile元素,属性将被设置但指令不会被引导.

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:鉴于 $compile 是实现此目的的唯一方法,有没有办法跳过第一次编译传递(元素可能包含多个子元素))?也许通过设置 terminal:true ?

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:我尝试将指令放入 select 元素中,正如预期的那样,编译运行两次,这意味着预期 select 的数量是预期的两倍代码>选项s.

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.

推荐答案

如果您在单个 DOM 元素上有多个指令,并且它们的应用顺序很重要,您可以使用 priority 属性来对其进行排序应用.较高的数字首先运行.如果不指定,则默认优先级为 0.

In cases where you have multiple directives on a single DOM element and where the order in which they’re applied matters, you can use the priority property to order their application. Higher numbers run first. The default priority is 0 if you don’t specify one.

编辑:经过讨论,这里是完整的工作解决方案.关键是移除属性:element.removeAttr("common-things");,还有element.removeAttr("data-common-things");(以防用户在 html 中指定 data-common-things)

EDIT: after the discussion, here's the complete working solution. The key was to remove the attribute: element.removeAttr("common-things");, and also element.removeAttr("data-common-things"); (in case users specify data-common-things in the html)

angular.module('app')
  .directive('commonThings', function ($compile) {
    return {
      restrict: 'A',
      replace: false, 
      terminal: true, //this setting is important, see explanation below
      priority: 1000, //this setting is important, see explanation below
      compile: function compile(element, attrs) {
        element.attr('tooltip', '{{dt()}}');
        element.attr('tooltip-placement', 'bottom');
        element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
        element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html

        return {
          pre: function preLink(scope, iElement, iAttrs, controller) {  },
          post: function postLink(scope, iElement, iAttrs, controller) {  
            $compile(iElement)(scope);
          }
        };
      }
    };
  });

工作 plunker 位于:http://plnkr.co/edit/Q13bUt?p=预览

Working plunker is available at: http://plnkr.co/edit/Q13bUt?p=preview

或者:

angular.module('app')
  .directive('commonThings', function ($compile) {
    return {
      restrict: 'A',
      replace: false,
      terminal: true,
      priority: 1000,
      link: function link(scope,element, attrs) {
        element.attr('tooltip', '{{dt()}}');
        element.attr('tooltip-placement', 'bottom');
        element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
        element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html

        $compile(element)(scope);
      }
    };
  });

演示

解释为什么我们必须设置terminal: truepriority: 1000(一个很高的数字):

Explanation why we have to set terminal: true and priority: 1000 (a high number):

当 DOM 准备好时,angular 会遍历 DOM 以识别所有已注册的指令并根据 priority 一一编译指令 如果这些指令在同一个元素上.我们将自定义指令的优先级设置为较高的数字,以确保它首先被编译,并且使用 terminal: true,其他指令将被跳过 编译此指令后.

When the DOM is ready, angular walks the DOM to identify all registered directives and compile the directives one by one based on priority if these directives are on the same element. We set our custom directive's priority to a high number to ensure that it will be compiled first and with terminal: true, the other directives will be skipped after this directive is compiled.

当我们的自定义指令被编译时,它会通过添加指令和删除自身来修改元素,并使用 $compile 服务来编译所有指令(包括那些被跳过的指令).

When our custom directive is compiled, it will modify the element by adding directives and removing itself and use $compile service to compile all the directives (including those that were skipped).

如果我们不设置 terminal:truepriority: 1000,则某些指令有可能在之前我们的自定义编译指示.当我们的自定义指令使用 $compile 编译元素时 => 再次编译已经编译的指令.这将导致不可预测的行为,特别是如果在我们的自定义指令之前编译的指令已经转换了 DOM.

If we don't set terminal:true and priority: 1000, there is a chance that some directives are compiled before our custom directive. And when our custom directive uses $compile to compile the element => compile again the already compiled directives. This will cause unpredictable behavior especially if the directives compiled before our custom directive have already transformed the DOM.

有关优先级和终端的更多信息,请查看如何理解`指令的终端`?

For more information about priority and terminal, check out How to understand the `terminal` of directive?

同样修改模板的指令示例是 ng-repeat(优先级 = 1000),当编译 ng-repeat 时,ng-repeat 在应用其他指令之前制作模板元素的副本.

An example of a directive that also modifies the template is ng-repeat (priority = 1000), when ng-repeat is compiled, ng-repeat make copies of the template element before other directives get applied.

感谢@Izhaki 的评论,这里是对 ngRepeat 源代码的引用:https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js

Thanks to @Izhaki's comment, here is the reference to ngRepeat source code: https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js

这篇关于从 AngularJS 中的指令添加指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆