AngularJS更改了编译指令的顺序

时间:2013-12-08 18:40:24

标签: javascript angularjs angularjs-directive angularjs-ng-repeat

据我所知,Angular以先到先得的方式编写内容,这有点棘手。我做了一个包含一些元素的指令,我希望有一个链接属性来查找内容中的东西。

对于具体的用例:我正在制作一个输入标签指令,该指令在内容中查找第一个输入,并将随机生成的id添加到inputfor属性为label

以下是代码:

// Find the first element with the attribute ng-label-target or the first input and links a label to it
app.directive('ngLabel', function () {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      label: '@',
    },
    template: '<span class="ng-label">' +
                '<label class="ng-label-text">{{label}}</label>' +
                '<span class="ng-label-content" ng-transclude></span>' +
              '</span>',
    link: function (scope, element, attrs) {
      scope.id = scope.id || 'random-id-' + Math.floor(Math.random() * 90000000);
      angular.element(element[0].querySelector('.ng-label-text')).
        attr({for:scope.id});

      var target = angular.element(element[0].querySelector('[ng-label-target]'));
      if (!target || target.length == 0) {
        target = angular.element(element[0].querySelectorAll('input,select,textarea')[0]);
      }
      target.attr({id:scope.id});
    },
  };
});

示例用法:

<ng-label label="Text:">
   <input type="text" ng-model="page.textColor" size="5" maxlength="7" placeholder="e.g. #000" required />
   <input ng-label-target type="color" ng-model="page.textColor" required />
</ng-label>

这本身就像一个魅力。

但是现在我想自动生成几个输入并使标签指向第一个输入。问题是,当我在ng-repeat内部执行ng-label时,我的代码会在处理完指令后生成ng-repeat代码,因此实际上找不到任何代码。

因此我的问题是:有没有一种方法可以指定角度来评估内部的嵌套指令而不是相反的方式?

或者,有没有比我现在做的更好的方法呢?

我做了一个小提琴来举例说明评估内容的顺序。你看到外部指令的值小于或等于它的内容(不能低于微秒,所以我不得不做一堆重复):

http://jsfiddle.net/YLM9P/

4 个答案:

答案 0 :(得分:19)

来自角度文档:

<强> PRIORITY 当在单个DOM元素上定义了多个指令时,有时需要指定应用指令的顺序。优先级用于在调用编译函数之前对指令进行排序。优先级定义为数字。首先编译具有更高数字优先级的指令。预链接功能也按优先级顺序运行,但后链接功能以相反的顺序运行。具有相同优先级的指令的顺序是未定义的。默认优先级为0。

<强> TERMINAL 如果设置为true,则当前优先级将是将执行的最后一组指令(当前优先级的任何指令仍将执行,因为相同优先级的执行顺序未定义)。

因此,对于您的问题,我认为将terminal属性设置为true将解决您的问题。

app.directive('ngLabel', function () {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    ....
    ....
    terminal: true
});

答案 1 :(得分:1)

与Angular中的许多其他问题一样,这个问题可以通过创建一个额外的指令来解决:

app.directive('myLabelTarget', function() {
 return {
   require: '^myLabel',
   link: function(scope, element, attrs, myLabelCtrl) {
     myLabelCtrl.doIfFirst(function(id) {
       attrs.$set('id', id);  
     });
   }
 };
});

使用require属性,您可以访问DOM中更高层的myLabel指令的控制器。

有关工作示例,请参阅此plnkr

答案 2 :(得分:0)

我认为你可以通过在父元素上添加一个函数表达式(可能传入一个参数),然后从子元素中调用该函数来大大简化你的代码并消除你的querySelectorAll(不是非常强烈的Angular-esque)在ng-repeat中。该函数只是设置一个你将使用的值,一旦它被设置,那么你知道你有第一个,其余的应该被忽略。如果您使用ng-repeat输入字段显示最终的HTML应该是什么样的话,我可能会更具体一些。

答案 3 :(得分:0)

您可以尝试注册MutationObserver,以侦听添加到DOM的元素。但这可能会给您的问题带来一点开销 - 您决定;)

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // you get notified about added DOM elements here
    // check mutation.type if it was an insertion
    // then you handle the first node specifically.
    console.log(mutation.target.nodeName, mutation.type, mutation);
  });
});

var config = {childList: true, attributes: false, characterData: false, subtree: false, attributeOldValue: false, characterDataOldValue: false};
observer.observe(element[0], config);
Demo              http://plnkr.co/h6kTtq
Documentation     https://developer.mozilla.org/en/docs/Web/API/MutationObserver
Browser support   http://caniuse.com/mutationobserver