For those who come across this question, I wanted to share the approach I took to solve it. While dluz's answer was helpful, I decided to take a more 'elegant' route.
My task involved using ng-repeat to populate options in a select dropdown and dealing with the incompatibility of ng-options with the jQuery Chosen plugin (a fantastic autocomplete/search dropdown plugin). I needed to initialize the plugin after all options had been rendered.
Here is the HTML snippet:
<select auto-complete ng-model="stuff">
<option ng-repeat="item in items" value="{{ item.value }}">{{ item.name }}</option>
</select>
And here is the JavaScript code:
// Extension of built-in ngRepeat directive - broadcasts to its parent when it is finished.
// Passes the last element rendered as an event parameter.
app.directive('ngRepeat', function () {
return {
restrict: 'A',
link: function ($scope, $elem, $attrs) {
if ($scope.$last)
$scope.$parent.$broadcast('event:repeat-done', $elem);
}
};
});
// Directive for working with the Chosen plugin.
app.directive('autoComplete', function () {
return {
restrict: 'A',
link: function ($scope, $elem, $attrs) {
$scope.$on('event:repeat-done', function () {
setTimeout(function () {
if (!$elem.children(':first-child').is('[disabled]'))
$elem.prepend('<option disabled="disabled"></option>');
$elem.chosen({ disable_search_threshold: 10, width: '100%' });
$elem.trigger('chosen:updated');
});
});
}
};
});
By extending the ng-repeat directive and adding my broadcaster function, I could easily detect when the ng-repeat operation was complete. This non-invasive addition can be applied universally to ng-repeat instances.
In the function itself, I used Angular's $scope.$parent
instead of $elem.parent().scope()
to access the parent context. This allowed me to broadcast a custom event (event:repeat-done
) and pass the last rendered element to the parent scope.
Subsequently, I could listen for this event in my auto-complete directive and initialize the Chosen plugin on the fully-rendered select element!
This technique can be adapted for any situation where you need to detect the completion of an ng-repeat directive. For example:
myapp.directive("myscroll", function () {
return{
restrict: "E",
transclude: true,
template: "<span class='left'></span><div class='mask' ng-transclude></div><span class='right'></span>",
link: function (scope, element, attr) {
scope.$on("event:repeat-done",function(){ console.log("ng-repeat rendered") })
}
}
})
EDIT: In some cases, detecting these events at the rootScope level may be useful. If so, you can replace $scope.$parent.$broadcast
with $scope.$emit
to allow the event to bubble up rather than down.