AngularJS: привязка к элементам в директиве, которая использует ng-repeat
У меня есть простая директива, в которой шаблон использует внутри него ng-repeat. Мне нужно запустить некоторый код, чтобы создать экземпляр компонента jquery для некоторых элементов, созданных директивой ng-repeat. Проблема в том, что если я положу этот код в функцию связи. Ng-repeat еще не создал эти элементы, поэтому ничего не создается.
App.directive('myDirective', ['$compile', '$timeout', function($compile, $timeout) {
return {
scope: {
domains: '='
},
templateUrl: '/app/partials/my_directive.html',
link: function($scope, element, attributes) {
element.find('.standard-help').tooltip('destroy');
element.find('.standard-help').tooltip({placement: 'top', trigger: 'click hover focus'});
}
};
}
Шаблон будет выглядеть следующим образом. Я пытаюсь прикрепить
<ul class="media-list domainList">
<li class="media" style="position:relative;" ng-repeat="domain in domains">
<a class="domainHeader" href="javascript://">
<span class="domainHeader">{{domain.tag}}</span>
</a>
<div class="media-body" style="margin-left: 52px;">
<ul class="standardsList">
<li ng-class="{ standardDisplayed: lessonLayout == 'standards' }" ng-hide="standard.lessons.length == 0" ng-repeat="standard in domain.standards">
<a href="javascript://" title="{{standard.description}}" ng-show="lessonLayout == 'standards'" class="standard-help pull-right"><i class="icon-question-sign"></i></a>
<h6 ng-show="lessonLayout == 'standards'">{{standard.tag}}</h6>
<ul class="lessonsList">
<li ng-class="{ lesson: true }" ng-repeat="lesson in standard.lessons" ng-click="onLessonSelected(lesson)">
<i class="icon-lock lesson-locked"></i>
<div>{{lesson.title}}</div>
</li>
</ul>
</li>
</ul>
</div>
</li>
</ul>
Я пробовал использовать $watch() и $observ() для регистрации обратного вызова, когда домены меняются и затем создаются код подсказки. Однако я не могу заставить его позвонить мне в нужное время. Любые идеи, что мне не хватает?
Ответы
Ответ 1
Я обнаружил, что при создании другой директивы, которую я добавил к элементу, где был создан ng-repeat, он будет уведомлен для каждого элемента, который был создан повторением. Тогда я мог бы просто $испускать событие, которое может прослушать родительская директива. В этот момент он может выполнять привязку к повторяющимся элементам. Это работало довольно хорошо, особенно для множественных ng-повторов внутри dom, потому что я мог разделить их по типу событий, который будет передан в новую директиву.
Вот моя директива:
App.directive("onRepeatDone", function() {
return {
restrict: 'A',
link: function($scope, element, attributes ) {
$scope.$emit(attributes["onRepeatDone"] || "repeat_done", element);
}
}
});
Вот использование этой новой директивы в шаблоне:
<ul>
<li on-repeat-done="domain_done" ng-repeat="domain in domains">...</li>
</ul>
Тогда внутри родительской директивы я мог бы сделать следующее:
link: function( $scope, element, attributes ) {
$scope.$on('domain_done', function( domainElement ) {
domainElement.find('.someElementInsideARepeatedElement').click(...);
} );
}
Ответ 2
$watch
должен быть в состоянии достичь того, что вы хотите сделать:
link: function(scope, elem, attrs) {
var init = function() {
// your initialization code
};
var ulElem = elem.children()[0]; // ul element
var unreg = scope.$watch(function() {
return ulElem.children.length === scope.domains.length; // check if all "li" are generated
}, function() {
// at this point, the ul is rendered
init();
unreg(); // unregister the watcher for performance, since the init function only need to be called once
});
}
Я продемонстрировал plunk, чтобы продемонстрировать это решение.
Ответ 3
Я думаю, что это из-за ошибки angular. Вы можете видеть здесь здесь
Одним из способов решения является удаление шаблона url и использование шаблона в самой директиве или использование $templateCache.get('/app/partials/my_directive.html')
Это сработало для меня:)
Ответ 4
Я просто столкнулся с этой точной проблемой и нашел то, что считаю лучшим решением. В принципе, вы можете использовать $timeout
для очереди задачи после завершения ng-repeat
рендеринга.
Примечание: это не означает, что вы фактически используете таймер или любую задержку времени; это просто простой способ добавить функцию в конец цикла $digest
.
Ниже приведен фрагмент моего кода. Директива использует ng-repeat
для отображения вкладок, поэтому мне нужно подождать до тех пор, пока она не будет показана для запуска моего метода tabSelected()
, который добавит класс active
к элементу HTML.
link($scope:ng.IScope, element:JQuery, attributes:ng.IAttributes):void {
if (this.autoActivateFirstTab && this.tabs && this.tabs.length > 0) {
var self = this;
var selectFirstTab = function() {
self.tabSelected(self.tabs[0]);
};
// Add a $timeout task to the end of the $digest loop to select the
// first tab after the loop finishes
self.$timeout(selectFirstTab, 0);
}
}
Я нашел решение из http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering, в котором есть живая демонстрация, который показывает это в действии.
Разделительные слова: Если вы тестируете свой код (как и должны) с помощью Karma, вам нужно будет вызвать $timeout.flush()
в своем тесте, чтобы убедиться, что он работает.
Мне пришлось запустить следующее в моем тесте, чтобы он работал правильно:
// Re-digest to ensure we see the changes
scope.$digest();
// Flush any pending $timeout tasks to ensure we see changes
try {
this.$timeout.verifyNoPendingTasks();
} catch ( aException ) {
this.$timeout.flush();
}
Ответ 5
$watch
должен работать в этом контексте. попробуйте