Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Infinite $digest Loop

I am working on a site where you can search a food, and see if its a fruit, a vegetable, or neither (because I'm bored). I decided to use Angular, even though I'm pretty new to it. I started getting this error: $rootScope:infdig Infinite $digest Loop

That's may or may not be the exact phrasing or the error, because the page lags out so much, I can't open the Javascript console.

This my result view controller:

app.controller('resultController', ['$scope', '$routeParams', '$http',
    function($scope, $routeParams, $http) {
        $scope.result = $routeParams.query;

        $scope.whatIsIt = function() {
            $http.get('json/foods.json').success(function(data) {
                var lists = JSON.parse(data);
                var itIsA = 'uuggghhhhh';

                if (lists['fruit'].contains($scope.result)) {
                    itIsA = 'fruit';
                } else if (lists['vegetable'].contains($scope.result)) {
                    itIsA = 'vegetable';
                }
            });
        }
    }]);

Heres my app module:

var app = angular.module('fruitOrNot', [
    'ngRoute'
]);

app.config(['$routeProvider' ,
    function($routeProvider) {
        $routeProvider.when('/', {
            templateUrl: 'layouts/fruit-search.html',
            controller: 'searchController'
        }).when('/:query', {
            templateUrl: 'layouts/result.html',
            controller: 'resultController'
        });
    }]);

And here is layouts/result.html:

<h1>{{ result }}</h1>
<p>{{ result }} is a {{ whatIsIt() }}</p>

So I am wondering what could be causing that $digest loop error, or how to find out, because I can't use the console. Thanks!

The whole project is here on Github as well.

like image 228
Addison Avatar asked Jul 06 '14 02:07

Addison


2 Answers

The problem that you were having was that you were setting a field on the scope, which implicitly calls $digest and lays out the template again. But, laying out the template makes the http request again, and then changes the scope, which calls $digest. And that is the infinite loop.

You can avoid this by ensuring that the http request never gets triggered during a layout of the template.

A more angular correct way of implementing your app would be to extract your GET request into a proper service and then injecting your service into the controller.

Then in the controller, you make the service call, like this:

app.controller('resultController', ['$scope', '$routeParams', 'whatsitService',
    function($scope, $routeParams, $http) {
        $scope.result = $routeParams.query;
        whatsitService.doit().then(function(result) {
            $scope.whatsit = result;
        }
    })
;

Your template would look like this:

<h1>{{ result }}</h1>
<p>{{ result }} is a {{ whatsit }}</p>
like image 132
Andrew Eisenberg Avatar answered Oct 19 '22 23:10

Andrew Eisenberg


The problem is you trying to call a function and publish the return value directly and the value that you are trying to return is out of scope. You need to put that value in scope.

Code snippet:

app.controller('resultController', ['$scope', '$routeParams', '$http',
function($scope, $routeParams, $http) {
    $scope.result = $routeParams.query;
    $scope.itIsA = "";
    $scope.whatIsIt = function() {
        $http.get('json/foods.json').success(function(data) {
            var lists = JSON.parse(data);
            var itIsA = 'uuggghhhhh';

            if (lists['fruit'].contains($scope.result)) {
                $scope.itIsA = 'fruit';
            } else if (lists['vegetable'].contains($scope.result)) {
                $scope.itIsA = 'vegetable';
            }               

        });
    }
}]);

and in the HTML Template:

p>{{ result }} is a {{itIsA}}</p>
like image 42
V31 Avatar answered Oct 20 '22 00:10

V31