0

I decided to start learning AngularJS by making a simple app.

The server-side application is built with ExpressJs, but the resource used below (/movie/:id) are not implemented yet, so pointing to this URL will result in a 404 (Not Found) error. So only getting '/' works.

I wanted to see how a $resource behaved so I made this simple test :

var app = angular.module("app", ["ngResource"]);

app.factory("Movie", function ($resource) {
    return $resource("/movie/:id");
})

app.controller("MovieCtrl", function($scope, Movie) {
    $scope.test = function () {
        Movie.query();
        return 42;
    } 
});

And my template file :

<div ng-app="app">
    <div ng-controller="MovieCtrl">
        {{ test() }}
    </div>
</div>

As expected the template is rendered and '42' is properly displayed, but if I watch the console in the Chrome developer tools I keep seeing the following error (also as expected):

GET http://localhost/movie 404 (Not Found)

But this message is printed indefinitely and never stops, as if my Movie resource keeps trying to reach /movie even though after a hundred tries it still keeps failing.

Thank you in advance.

Laurent Dezitter
  • 710
  • 1
  • 8
  • 16

1 Answers1

2

This is because Movie.query() calls $scope.$apply() after it gets response from the server.

Everytime $scope.$apply() is called, angular does dirty checking (which again invokes test and therefore calls Movie.query() again) to find out if anything has changed. This causes an infinite loop.

move Movie.query() out from the test(), and this should work.

Let me make myself clear - take look at this pseudo code:

var watches = ['$scope.test()'];
var previous = {};
var values = {};

$rootScope.$apply = function(){
  previous = values;
  values = {};
  var dirty = false;  
  for (var i =0;i<watches.length;i++){
      var expression = watches[i];
      values[expression] = value = eval(expression);
      if(value!=previous)dirty=true;
  }
  if(dirty)$rootScope.$apply();
}

Movie.query = function(){
   setTimeout(function(){
      $rootScope.$apply();
   },300);
}

$scope.test = function(){
   Movie.query();
   return 42;
}

so the flow is following:

  1. $scope.apply();
  2. $scope.test();
  3. Movie.query(); -> setTimeout($scope.apply,100) (back to beginning );

and so on..

g00fy
  • 4,717
  • 1
  • 30
  • 46
  • Is the dirty checking performed to see if the result of the test method returned has not changed ? If so the Movie.query() does not alter this result. – Laurent Dezitter Jun 22 '13 at 05:23
  • 1
    Yes, but `Movie.query()` triggers `$scope.$apply()` (when the data comes back from the server) which checks if `test()` has changed. During this check, `test()` triggers another `Movie.query()` which again triggers `scope.$apply()`, and so on. – g00fy Jun 22 '13 at 12:18