0

In one of my controller functions in my angular.js application, I'm trying to use Google's RSS Feed API to fetch feed entries to populate in my scope.items model.

It's behaving quite strangely, with the console output in the innermost part always being '2' but while the console output in the outermost loop being '0' and '1' (which is correct since that is the length of my items array).

I'm thinking it could have something to do with the Google API thing being an async request and that it hasn't finished before I try to manipulate it (?). Still doesn't make sense that my iterator variable would become '2' though!

Code:

function ItemListCtrl($scope, Item) {
$scope.items = Item.query({}, function() { // using a JSON service
    for (var i = 0; i < $scope.items.length; i++) { // the length is 2 here.
        $scope.items[i].entries = [];
        console.log(i); // gives '0' and '1' as output in the iterations.
        var feed = new google.feeds.Feed($scope.items[i].feedUrl);
        feed.load(function(result) {
            console.log(i); // gives '2' as output in both the iterations.
            if (!result.error) {
                for (var j = 0; j < result.feed.entries.length; j++) {
                    $scope.items[i].entries[j] = result.feed.entries[j];
                }
            }
        });
    }
});

}

joakimnorberg
  • 655
  • 3
  • 10
  • 16

1 Answers1

0

The callback function is executed asynchonously, after the loop over the items has ended. And at the end of the loop, i is equal to 2, since there are 2 items in your items array.

See Javascript infamous Loop issue? for another example of the same behavior, and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures#Creating_closures_in_loops.3A_A_common_mistake for an more in-depth explanation.

Community
  • 1
  • 1
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks, got it to work, so within the scope of my controller it works fine now. But - when the view is rendered the data is not yet ready. Same problem here I guess, but when it comes to Angular, how do I make sure the data is loaded before the view is rendered, without using a service? – joakimnorberg Jul 21 '13 at 18:04
  • Why should the data be loaded before the view is rendered? If you think it'll take too much time before the data is available, display a static "please wait while loading" message, and hide it as soon as the data is made available in the scope. – JB Nizet Jul 21 '13 at 18:16
  • Yeah, but how do I accommodate that in Angular? I'm using an ngRepeat over my item.entries array, which will be empty at the time of rendering. – joakimnorberg Jul 21 '13 at 18:18
  • That's the magic of Angular. Angular watches all the changes in the scope. As soon as the callback function will replace the empty list in the scope by a list full of entries, the ngRepeat directive will re-render the list and display all the items automatically. you just need `$scope.items = ...;` inside the callback function. – JB Nizet Jul 21 '13 at 18:22
  • Then I think the problem is that my entries array is not declared within $scope.items. $scope.items is bound using a service with an underlying JSON file. The entries array is created in the controller. Bad modelling - yes, but do you have a suggestion on how to get around it? – joakimnorberg Jul 21 '13 at 18:26
  • I told you something wrong. Since you're using a non-angular way of sending AJAX requests, you'll aslo need `$scope.$apply()` after you've set the list to the $scope. – JB Nizet Jul 21 '13 at 18:28