1

I am new in AngularJS world. I have a circumstance here.

<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="myApp">

<table ng-controller="myCtrl" border="1">
<tr class="x" ng-repeat="x in records">
  <td>{{x.Name}}</td>
  <td>{{x.Country}}</td>
</tr>
</table>

<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
  $scope.records = [
    {
      "Name" : "Alfreds Futterkiste",
      "Country" : "Germany"
    },
    {
      "Name" : "Berglunds snabbköp",
      "Country" : "Sweden"
    },
    {
      "Name" : "Centro comercial Moctezuma",
      "Country" : "Mexico"
    },
    {
      "Name" : "Ernst Handel",
      "Country" : "Austria"
    }
  ];
  var arrays = document.getElementsByClassName("x");
  console.log(arrays.length);
});
</script>

I tried to get the array of html elements I did load by angularJS (using ng-repeat). When I logged the value of whole array, its results are correct. However when I logged the array.length or array[1], the results were 0 and undefined respectively. I tried to put these code in $scope.on('viewContentLoaded') to ensure that all html elements finished loading, but the results were the same. So what is my stupidity here?

Thank you.

**** Solution:

 $timeout(function(){
 var arrays = document.getElementsByClassName("x");
 console.log(arrays.length);
 });

Thanks to @Vladimir M

Justin
  • 481
  • 1
  • 6
  • 11

4 Answers4

1

One thing to remember, is that angular is doing certain things async. When meaning, that when you assign your records in your controller, angular will only start rendering after your controller method is completed. Hence, your request for elements of the document will return nothing.

One way to go around it in certain cases is to use $timeout service.

$timeout(function(){
 var arrays = document.getElementsByClassName("x");
  console.log(arrays.length);
});

The method within timeout should be executed when the angular digest cycle is done rendering.

Vladimir M
  • 4,403
  • 1
  • 19
  • 24
  • it worked for me. But I want to dig a little deeper to understand more. As I knew, $timeout service is similar with setTimeOut function, which mean it will allow us to execute a function after delay time. So how do angular know that all views finish loaded then run the $timeout service? I thought of the case that all views hadn't finished loading already, so I tried to put these code inside the $scope.on("viewContentLoaded"), angular.element(document).ready, and $(document).ready (jQuery) to ensure everything done with loading, but it didn't work here. thanks for helping me out! – Justin Sep 18 '16 at 17:00
  • it has to do with the digest cycle within angular. rendering of the particular element for which you are creating a controller will happen before all the timeouts that were created within that controller method. However, things get more complicated, when template will include some other elements with own controllers, as those will most probably be processed after given timeout is scheduled. – Vladimir M Sep 18 '16 at 17:03
  • Oh, I understood. Thank you so much. – Justin Sep 18 '16 at 17:19
  • Hello again. I have a question. If I want to get these html elements by AngularJs only (not use any selector of javascript or jquery), so any methods to achieve it? Thank you. P/S: If I bother you too much, just ignore this question. I just want to understand more about AngularJS. So sorry. – Justin Sep 18 '16 at 17:48
  • I haven't been doing that too much, cause native or jQuery API were enough for me. You will be able to get from angular a reference to the element for the current scope with this: [link](http://stackoverflow.com/questions/12960701/how-do-i-get-current-scope-dom-element-in-angularjs-controller), however, not for elements that do not have their own scope. Personally, I don't see a good reason not to use jQuery or document.getElement.... API. – Vladimir M Sep 18 '16 at 17:54
  • I see the point. You're right when we can use js or jquery for getting dom structure without any harm. I only want to learn more about angularJS. Thanks for your enthusiasm. Have a good one. – Justin Sep 18 '16 at 19:18
0

You need to wrap the code inside

angular.element(document).ready(function(){});

like this

angular.element(document).ready(function(){
var arrays = document.getElementsByClassName("x");
console.log(arrays[2]);
});

You can refer to the Demo

Also I tried your code with logging whole array or array.length but it was not working. Actually it will not work until DOM get rendered. So the above code will wait till document is ready, once document get rendered you can access html elements.

Dashing Dev
  • 105
  • 6
  • hmm.. It worked on plnkr, but not work in my local and w3school try out. Super weird. – Justin Sep 18 '16 at 16:34
  • You can try changing angular.min.js script reference. Actually I updated that too in plnkr. In my case, it was not working with the reference that you have in your code. – Dashing Dev Sep 18 '16 at 16:57
0

Angular is an MVC framework. MVC stands for 'Model View Controller', meaning your application should be divided into 3 parts:

  1. The view (any directives in the html, for example ng-repeat).
  2. The controller (your app.controller, controls scope and )
  3. The model (in angular, service / factory), which stores business logic and functionality. for example, in your case, an angular factory might return an array of users (name, country), which your controller will pass into $scope, which will then be transferred to your view automatically.

just to point out, each of these (view, controller, model) go in a different file by convention.

another notable feature of angular, as you might know already, is two-way binding. this means that any change in the view is reflected in the model and controller and vice-verse. What that means for you is that your ng-repeat will always represent exactly what you have in $scope.records. just console log this.

angular sometimes works in mysterious ways and was not meant for you to use selectors on. it is never necessary using angular, which is another advantage of angular in general. no selectors!

nadavvadan
  • 3,930
  • 1
  • 17
  • 29
  • Yes, I knew the theory, but I just think it is similar with javascript and jQuery; when everything finished loaded, we can manipulate DOM. hmm. I just want to learn more about angular then. Thank yoi – Justin Sep 18 '16 at 16:38
  • @Justin yes you can manipulate DOM. The MVC architecture just assumes you aren't suppose to. Check this great answer about how AngularJS philosophy differs from the jQuery's one - http://stackoverflow.com/a/15012542/3091161 – enkryptor Sep 18 '16 at 17:19
0

You are trying to get the li elements before they render. S0 as @DashingDev said, you need to move your block of code into angular.element(document).ready(function(){});. Your code should be look

angular.element(document).ready(function(){
  var multibutton = document.getElementsByClassName("x");
    console.log(multibutton[0]);
}); 

Here is the Working fiddle

Mr.7
  • 2,292
  • 1
  • 20
  • 33