2

I have a ng-repeat directive which displays a list of links. It works fine, but my code also has a javascript function which places the list depending on its dimensions. Naturally, the dimension of the list is not known until Angular is done databinding and modifying the DOM. If I call the function immediately after data change, then the placement calculation jumps the gun and places the list as if it's empty. I want to fire the function once Angular is done modifying the DOM. How can I do that?

EDIT:

Just tried ngcloak as a possible solution, unfortunately it does not work. Reading ngcloak's documentation, my problem is not that the list flickers, but it is placed as if it's empty, and then stays there. I need a way to call my function once the databinding is complete.

Boluc Papuccuoglu
  • 2,318
  • 1
  • 14
  • 24
  • When the data change occurs, use $timeout service to call the dimenstion modifier after a delay? I do not know if you have any event that is fired by angular when it finishes the dom manipulation – Ranjith Ramachandra Oct 04 '13 at 14:19
  • @RanjithR, I did think of using $timeout, but it seemed a poor choice because either I will have to set a large value for the interval to be on the safe side (which WILL produce a flicker), or when the list is large the function will be called when Angular is still modifying the DOM and the list will be placed as if it is smaller than it actually is. But that is the way to go I guess if no one can suggest a deterministic way of calling a function when databinding is done. – Boluc Papuccuoglu Oct 04 '13 at 14:24
  • There is a way. You could write a recursive function that calls itself after a short timeout like 100ms if angular is in digest or apply phase. If it is not in digest or apply phase, then you can call the placement. – Ranjith Ramachandra Oct 04 '13 at 14:28
  • @RanjithR, I think yes, that will work! How can I check if angular is in apply phase? – Boluc Papuccuoglu Oct 04 '13 at 14:30
  • @RanjithR, it works! If you post your suggestion as an answer, I will upvote it and accept. – Boluc Papuccuoglu Oct 04 '13 at 15:23
  • glad to have helped :) did that – Ranjith Ramachandra Oct 04 '13 at 20:28

5 Answers5

1

I've done this before by creating a custom directive to use in addition to ng-repeat. So for example

<div ng-repeat="foo in bar" myDirective>

What that gives you is that in myDirective you can access the directive's scope and check for the last item in the ng-repeat render. For example this is what I have for using the isotope jquery library on some elements rendered by ng-repeat:

angular
    .module('myModule')
    .directive('myDirective', function ($timeout) {
        return {
            restrict: 'A',
            link: function postLink(scope, element, attrs) {
                if (attrs.ngRepeat && scope.$last) {
                    $timeout(function() {
                        $('#myContainer').isotope({
                           layoutMode: 'fitRows'
                        });
                    });
                }
            }
        };
    });

This used with html like:

<div id="myContainer" ng-repeat="foo in bar" myDirective>
    <div class="element">{{foo}}</div>
</div>

Should give you what you're looking for.

Just replace $('#myContainer').isotope({}) with what you want to execute when ng-repeat has finished rendering your repeat.

Mike Driver
  • 8,481
  • 3
  • 36
  • 37
1

seems you're using a masonry-like library for positioning elements, if so, use angular masonry directive for that. there is fork that eliminates the jQuery too.
but anyway, take a look at it, will be useful.

  • for text data a small delay would work, like:

    timeout = $timeout(function layout() {
      //layout elements 
    }, 30);
    
  • for images you can get the dimensions before completely loading image , here you can find a good way.

Community
  • 1
  • 1
Reyraa
  • 4,174
  • 2
  • 28
  • 54
1

For anybody that stumbles upon this question looking for a way to execute a function after Angular is done updating the view because of data changes (not only after page load):

If you use $timeout with the timeout=0 (or omit the second argument - it's the default value), the code will be executed in the next digest cycle.

$timeout(function(){
    /// your logic here
}, 0);

In the situation described in the question, this should be called in a custom directive, as described by @MikeDriver

Why not probe angular's current phase?

According to another SO answer, Angular's authors say that $phase should not be used -it is an internal mechanism and is not future-safe. https://stackoverflow.com/a/21611534/1578982 Obviously the $timeout way is also simpler.

Community
  • 1
  • 1
Pietro
  • 121
  • 5
0

I think ngCloak is what you are looking for.

Phil Sandler
  • 27,544
  • 21
  • 86
  • 147
  • Nope, tried it and it doesn't work. See my Edit to the original post. Thanks for the link tho, interesting directive, good to know about it. – Boluc Papuccuoglu Oct 04 '13 at 14:15
0

When the data change occurs, use $timeout service to call the modifier after a delay. If timeout is not reliable enough because you can't guess the time correctly, you could write a recursive function that calls itself after a short timeout like 100ms to check if angular is in digest or apply phase. If it is not in digest or apply phase, then you can call the placement modifier.

Ranjith Ramachandra
  • 10,399
  • 14
  • 59
  • 96