2

I have a simple Angularjs app with a controller containing a scope list of strings called id:

angular.module('app', [])
.controller('appCtrl', ['$scope', '$timeout',
  function($scope, $timeout) {
    $scope.ids = ['id1', 'id2'];
  }]); 

In the html partial an ngrepeat iterates over ids and add this ids to a list of divs:

<div ng-app="app">
<div ng-controller="appCtrl">

<div ng-repeat="id in ids" ng-attr-id="{{id}}">
  div-{{id}}
  </div>

  </div>
</div>

These divs can be selected with angular.element in the controller only when it is included in a $timeout:

$timeout(function(){
 console.log('with timeout, element found');
 console.log(angular.element('#'+$scope.ids[0]));
});

console.log('no timeout, element not found');
console.log(angular.element('#'+$scope.ids[0]));

Here is a working example: https://codepen.io/neptune01/pen/zjqGzO

What exactly causes this asynchronous flow? Why do I need timeout to select the elements?

I'm trying to get rid of timeouts where possible. Is there a better way to do this element selection, without timeout eventually?

neptune
  • 1,141
  • 2
  • 14
  • 25

2 Answers2

2

As I heard and read, it is a problem with AngularJs. The controller codes, execute before DOM complete, so without $timeout, there is no id '#'+$scope.ids[0] to select. You must use $timeout to select and use that.

For more information search and read about digest cycle of angularjs.

Saeed
  • 4,479
  • 1
  • 23
  • 36
1

@Saeed.At already pointed in the right direction.

It has something to do with AngularJS digest cycles. ng-repeat, ng-include and couple of other directives as well causing their rendering to be queued up and rendered in the following digest cycle. So for example

  • Contoller & Template rendering
  • ng-repeat rendering

Now you already see that trying to access ng-repeat items directly at start won't work since they will only be rendered one cycle later.

The $timeout basically does the same. If you don't provide a timeout parameter it will be queued up after the next digest cycle. So the chain will look sth like this

  • Contoller & Template rendering
  • ng-repeat rendering
  • $timeout function

Also important to note that if your ng-repeat contains other ng-includes or certain directives they will queue up themselves as well, so it could be that a single $timeout won't be enough. For this cases you'd have to take a different approach

Nicolas Gehlert
  • 1,857
  • 15
  • 33