AngularJS изменяет порядок составления директив

Насколько я заметил, Angular компилирует вещи на основе первого, первого порядка, что довольно сложно. Я сделал директиву, которая обертывает некоторые элементы, и я хочу иметь свойство ссылки, которое ищет материал в контенте.

В конкретном случае использования: я делаю директиву метки ввода, которая просматривает содержимое для первого ввода и добавляет случайно сгенерированный id в атрибут input и for к 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 генерируется после того, как моя директива была обработана, поэтому ничего не найдено.

Поэтому мой вопрос: есть ли способ, которым вы можете указать Angular для оценки вложенных директив наизнанку, а не наоборот?

Или, есть ли способ сделать это, чем я сейчас делаю?

Я сделал скрипку, чтобы продемонстрировать порядок, в котором материал оценивается. Вы видите, что внешняя директива имеет меньшее или равное значение, чем содержимое (не может идти ниже микросекунд, поэтому мне пришлось сделать кучу повторов):

http://jsfiddle.net/YLM9P/

Ответы

Ответ 1

Из документов angular:

ПРИОРИТЕТ Когда существует несколько директив, определенных для одного элемента DOM, иногда необходимо указать порядок, в котором применяются директивы. Приоритет используется для сортировки директив до вызова функций компиляции. Приоритет определяется как число. Сначала устанавливаются директивы с большим числовым приоритетом. Функции предварительной ссылки также выполняются в порядке приоритета, но функции пост-ссылки выполняются в обратном порядке. Порядок директив с таким же приоритетом undefined. Приоритет по умолчанию - 0.

ТЕРМИНАЛ Если установлено значение true, то текущим приоритетом будет последний набор директив, которые будут выполняться (любые директивы в текущем приоритете все равно будут выполняться, так как порядок выполнения с таким же приоритетом равен undefined).

Итак, для вашей проблемы, я считаю, что установка свойства terminal в true решит вашу проблему.

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

Ответ 2

Эта проблема, как и многие другие в Angular, может быть решена путем создания дополнительной директивы:

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

С атрибутом require вы можете получить доступ к контроллеру директивы myLabel выше в DOM.

См. plnkr для рабочего примера.

Ответ 3

Я думаю, что вы можете значительно упростить свой код и устранить свой запросSelectorAll (не ужасно Angular -esque), добавив выражение функции в родительский элемент (возможно, передав параметр), а затем вызвав эту функцию из дочернего элемента ( s) в ng-повторе. Функция просто установила значение, которое вы использовали бы, и как только оно будет установлено, вы знаете, что у вас есть первый, а остальные должны быть проигнорированы. Я мог бы быть более конкретным, если бы вы показали, как должен выглядеть последний HTML-код с вашими полями ввода ng-repeat.

Ответ 4

Вы можете попытаться зарегистрировать 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);
<Предварительно > Демо http://plnkr.co/h6kTtq Документация https://developer.mozilla.org/en/docs/Web/API/MutationObserver Поддержка браузера http://caniuse.com/mutationobserver