AngularJS - How do I avoid using $timeout to wait for an element to be created?

Georgi Angelov picture Georgi Angelov · Jul 12, 2013 · Viewed 9.5k times · Source

Here is the idea:

So I am trying to use an external library function to create some manipulation in the DOM in one of my controllers that my ng-repeat is connected to.

Newest jsFiddle working but ...

http://jsfiddle.net/GeorgiAngelov/KRbdL/150/

So basically I got it to work with @BrandonTilley's help but I am trying to avoid using $timeout as I don't think that it is a viable solution but a hack.

New fiddle with the help of @BrandonTilley

added onload listener and also I am trying to get the new element or its parent by getElementById but all it returns is NULL.

http://jsfiddle.net/GeorgiAngelov/KRbdL/143/

The problem is the following: I am calling that external function in my controller that adds elements to the ng-repeat array , which in term adds the new elements to the DOM. However, when I am inside the controller, the element does not exist yet, even though I have added it to the array.

How can I bound an external function to be called once the element has actually been appended to the DOM, rather than when it was actually added to the array that controls the ng-repeat?

The problem comes is that I have template 1 calling template 2, then template 2 calls template3 and then template3 calls back template 2, and I want to connect an element from template3 into template2 once template3's finishes rendering template2.

I created a directive that is linked to template3 and I used the $last property but it is still not working because template3 loads but I do not know when template2 finishes loading.Furthermore, the element.ready() does not even fire up. I also tried hacking it around with $timeout and removing element.ready, but it still gave me the error that the child element did not exist yet. I do not want to use $timeout and so I am looking for a more functional solution to my problem.

Also, I tried calling the jsPlumb library when I call the function to create a new firstlevel element but it gives parentNode undefined. I commented it out in addChild function .

Directive that is used in template 3 on the ng-repeat ng-include=template2

app.directive('vectorDrawDirective', function($timeout) {
    return function($scope, element, attrs) {
        //angular.element(element).css('color','blue');
        if ($scope.$last === true) {
            element.ready(function() {
            jsPlumb.connect({
                    source: $scope.firstlevel.parent_id,
                    target: $scope.firstlevel.id,
                });
                jsPlumb.repaintEverything();    
        })
        }
    }
});

Here is a diagram I did to help you visualize what I am trying to acomplish ( look at the text on the top right corner )

enter image description here

Answer

Michelle Tilley picture Michelle Tilley · Jul 12, 2013

ngInclude provides an onload attribute which will evaluate an expression when it is loaded; does that give you what you need?

<div vector-draw-directive class="thirdlevel"
  ng-repeat="firstlevel in secondlevel.children"
  ng-include="'templateLevel2.html'" onload="loaded()">
</div>

Very basic example: http://jsfiddle.net/BinaryMuse/KRbdL/121/