AngularJs: Run Directives after View is fully rendered

I'm new to all Angular World, and i'm facing a problem managing the directives.

I'm working on a project that uses Tabs, and i want to extend its functionality to handle overflowed tabs when window size is narrower that the width of all my tabs, so i need to calculate some dimensions of elements to achieve this. The tabs are built from an Object in $scope, the problem is that the directive that calculates the dimensions is run before the view is fully compiled.

Plnkr Link: http://plnkr.co/edit/LOT4sZsNxnfmQ8zHymvw?p=preview

What i've tried:

  1. loading the template in directive using templateUrl
  2. working with transclude
  3. to use $last in ng-repeat
  4. try to reorder directive and create a dummy directive in every tab to trigger an event

I think that there is some AngularJs event, or property to handle this situation.

Please Guys Help :)

AngularJS does not seem to have reliable post-render callbacks for directives used within ng-repeat. [1]

Maybe you can solve this on CSS level by adding a "responsive" overflow-control element for specific screen widths.

  1. https://groups.google.com/forum/#!topic/angular/SCc45uVhTt8

Update: There now is a way to do this using nested $timeouts. See: http://lorenzmerdian.blogspot.de/2013/03/how-to-handle-dom-updates-in-angularjs.html

You can add a watch on the actual DOM elements in order to know when your ng-repeat is loaded into the DOM.

Give your ul an id, say #tabs

$scope.$watch(
    function () { return document.getElementById('tabs').innerHTML },  
    function(newval, oldval){
        //console.log(newval, oldval);
        //do what you like
    }, true);

I make a branch on your plunker, I I think this is what you were looking for

I change your directive to this

.directive('onFinishRenderFilters', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                });
            }
        }
    }
});

Then on your HTML I added the directive

 <li ng-repeat="tab in tabs" on-finish-render-filters>

And the last thing I put the code I want to run after the repeat finish

$scope.$on('ngRepeatFinished', function (ngRepeatFinished) {
           $scope.tabs = [{
        index: 1,
            title: "Tab 1",
            link: "/tab1/"
        },{
            index: 2,
        title: "Tab 2",
            link: "/tab2/"
        },{
            index: 3,
        title: "Tab 3",
            link: "/tab3/"
        },{
            index: 4,
        title: "Tab 4",
            link: "/tab4/"
        }];

  });

http://plnkr.co/edit/TGduPB8FV47QvjjLnFg2?p=preview

According to your comments, you already have a solution that works ... and its currently the only solution that I know of: $timeout.

The question is, what do you want to achieve? You want a callback when all rendering is done. Ok, good, but how can angular know this? In a modern app which you are apparently developing when using angular, a rerender can happen all time! A js event can occur where data is loaded from backend, even right after initializing the app, which leads to rerendering. A mouse movement by the user can trigger rerendering of elements, which are even outside of angulars scope because of CSS rules. Any time, a rerender can happen.

So, with these considerations what can angularJS tell you about when rendering is done? It can only tell you, when current $apply and/or $digest chain is processed aka the browser event queue is empty for now. Exactly that is the only information that angularJS knows of. And you use $timeout with a delay of 0 for this.

Yes, you are right, in a bigger appliataion, that can be tricky to a point where its not reliable anymore. But in this case, you should think about how you can solve this in another way. F.i. if a rerender happens later, there must be a cause, like new data that is loaded from the backend. If so, you know when the data is loaded and therefore a rerender happens and therefore you know exactly when you have to update your widths.