How can I get the element descriptor of ng-repeat child elements in my custom directive?

I am trying to create a custom directive that rebuilds its contents as a grid. I want to pass the result of the ng-repeat directive, and then reorder the resulting elements.

The problem is that I call the element.children() method inside the link function, I have an empty array, because the ng-repeat directive is not yet displayed and is interpreted as a comment.

Otherwise, the directive works fine if its contents are "static".

HTML

 <grid n='6'> <div ng-repeat="i in [1,2,3]"></div> </grid> 

My directive contains only interesting snippets of code:

 app.directive('grid', [function () { return { restrict: 'E', replace: true, transclude: true, template: "<div ng-transclude></div>", link: function (scope, grid, attrs) { // With an ngRepeat transcluded, els result in an empty array var els = grid.children(); // ... }; }]); 

What am I missing?

+6
source share
2 answers

To achieve reordering, you have several options:

  • Manipulate the DOM. IMHO, this is the least preferred way, it is not very angular.
  • Reorder the array ([1, 2, 3]) used by ngRepeat in your bind function.
  • Use filter orderBy ( doc )

I created a plunger to demonstrate 2 and 3, hope this helps. http://plnkr.co/edit/vrgeBoJZiG6WMu4Rk46u?p=preview

0
source

There are several ways to approach this. The simplest and most common solution you will see is the comments offered - the leverage of $ timeout per se ...

 .directive('grid', ['$timeout', function ($timeout) { return { restrict: 'E', replace: true, transclude: true, template: '<div ng-transclude></div>', link: function (scope, grid, attrs) { $timeout(function() { console.log(grid.children()); }); } } }]); 

$ timeout ([fn], [delay], [invokeApply], [Pass]);

[...]

invokeApply - If set to false, will skip the model test, otherwise fn is called in the $ apply block. ( default: true )

Calling $timeout will cause the $digest loop, so by the time you register the children, the ng-repeat directive will be executed. The race with ng-repeat is the main topic of this problem, because it still does the job by the time we enter our link function.


Another way you can solve this, which, of course, is less common - but it does an excellent job of showing a more detailed sequence of events, looks like this:

 .directive('grid', [function () { return { restrict: 'E', replace: true, transclude: true, template: '<div ng-transclude></div>', link: function (scope, grid, attrs) { scope.$on('repeat-done', function () { console.log(grid.children()); }); } } }]) .directive('ngRepeat', [function () { return { restrict: 'A', link: function (scope, elem, attrs) { if (scope.$last) scope.$root.$broadcast('repeat-done'); } }; }]); 

Here we unobtrusively extend ng-repeat to call the function upon completion - and we can subscribe to it via $on in our link function.


JSFiddle Link - a simple demo demonstrating both approaches

0
source

Source: https://habr.com/ru/post/950358/


All Articles