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