AngularJS ngIf предотвращает поиск элемента внутри директивы
У меня есть директива AngularJS, которая включает ngIf
, и я хотел бы изменить часть DOM внутри ngIf
в функции ссылки. К сожалению, кажется, что ngIf
не позволяет мне находить элементы DOM внутри него в функции ссылок.
Вот код для директивы:
directive('column', function () {
return {
templateUrl: 'views/column.html',
restrict: 'E',
scope: {
column: '='
},
controller: ['$scope', function ($scope) {
$scope.editing = true;
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
};
}],
link: function postLink(scope, element) {
var select = element.find('select');
console.log(select); // See if it can find the select element
// var types = scope.column.types();
// add types as options to the select element
}
};
});
И вот упрощенный html директивы:
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-if="editing">
<select></select>
</form>
</div>
Вот ссылка на пример jsFiddle http://jsfiddle.net/dedalusj/Y49Xx/1/
Вызов element.find
в функции link возвращает пустой массив, но как только я удаляю ngIf
из формы, он возвращает правильный элемент DOM. У меня такое чувство, что я делаю это неправильно.
UPDATE
Спасибо за ответы, но я нашел другое решение. Я просто создал другую директиву, которая инкапсулирует форму, добавила ее в шаблон шаблона column
с помощью ng-if="editing"
.
Директива формы не имеет собственной области видимости, поэтому она эффективно работает из области действия column
и всегда имеет доступ к элементу select
, поскольку она находится внутри своего дерева DOM. Я оплачиваю дополнительную директиву, но мне не нужно использовать взломать $timeout
. Я создал новый jsFiddle, чтобы проиллюстрировать решение http://jsfiddle.net/dedalusj/nx3vX/1/
Спасибо @Michael, но я не могу просто использовать ng-option
, потому что массив types
поступает из XML файла, а его элементы - другие angular.элементные объекты, которые нельзя легко вставить с помощью ng-option
.
Ответы
Ответ 1
Директива ngIf
работает с использованием функции пересылки Angular. Что происходит во время цикла компиляции/ссылки:
- Содержимое внутри
ngIf
удаляется из DOM при компиляции
- Angular выполняет функции связи. Функция ссылки
ngIf
запускается до
функция связи используемой директивы. Когда функция ngIf
link
работает, он использует $scope.$watch()
для просмотра значения ng-if
атрибут.
- Выполняется функция вашей директивной ссылки, в этот момент содержимое
ngIf
не является частью DOM
- Вызывается часы, созданные на шаге (2), и
ngIf
затем вызывает функцию $transclude
, чтобы вставить содержимое ngIf
в DOM, если значение атрибута ng-if
правдиво.
- Будут выполняться любые функции часов,
$timeout
или использование $scope.$evalAsync
, которые вы зарегистрировали в своей функции ссылки.
Итак, если вы хотите получать доступ к элементам внутри содержимого ngIf
, код должен запускаться после шага 4 выше. Это означает, что любые функции, зарегистрированные в $scope.$watch
, $timeout
или $scope.$evalAsync
в вашей функции директивной ссылки, будут работать. Для одноразового фрагмента кода установки я бы выбрал $scope.$evalAsync
:
angular.directive('yourDirective', function () {
return {
...
link: function(scope, elem) {
scope.$evalAsync(function () {
// code that runs after conditional content
// with ng-if has been added to DOM, if the ng-if
// is enabled
});
}
};
});
Ответ 2
Как сказал @moderndegree, ngIf
удаляет элемент, к которому он применим, из DOM, так что вы не сможете его найти, когда он там отсутствует. Но вы могли бы написать свою директиву таким образом, чтобы обходиться следующим образом:
controller: function ($scope, $element, $timeout) {
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
$timeout(function() {
var select = $element.find('select');
select.append('<option>Value 1</option>')
.append('<option>Value 2</option>')
.append('<option>Value 3</option>');
});
};
}
Обновлен jsFiddle здесь.
Трюк здесь заключается в задержке вызова find()
с помощью $timeout
с интервалом 0, чтобы ждать Angular для обновления DOM.
UPDATE
После того, как вы подумали над своим кодом, я понимаю, что, возможно, вы можете позволить Angular сделать тяжелую работу для вас:
Javascript
directive('column', function () {
return {
templateUrl: 'views/column.html',
restrict: 'E',
scope: {
column: '='
},
controller: ['$scope', function ($scope) {
$scope.editing = true;
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
};
}],
};
});
HTML
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-if="editing">
<select ng-model="type" ng-options="type for type in column.types"></select>
</form>
</div>
jsFiddle
Теперь вам не нужно беспокоиться о том, чтобы найти элемент select
в нужное время и заполнить его. Angular делает все это для вас.:)
Ответ 3
Вы можете поместить свой код из функции ссылок внутри $timeout.
$timeout(function(){
var select = element.find('select');
console.log(select);
});
Не забудьте ввести $timeout в свою директиву
directive('column', function ($timeout) {
Ответ 4
Я столкнулся с этой же проблемой, и я смог ее решить с помощью ng-show, это предотвратит эту проблему, потому что ngIf удаляет элемент, который он применил к DOM, поэтому вы не сможете найти его, когда он не будет.
поэтому в вашем случае:
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-show="editing">
<select></select>
</form>
будет работать нормально.
Приветствия.