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