3

I am simply trying to load data when my app starts. However, the view loads faster than the http request(of course). I want to refresh my view once my data has been properly loaded because that data defines my view.

I've tried $rootScope.apply from inside the factory where I do my http request, and I also tried directly doing the http request in my controller again with $scope.apply, and neither one worked as they both gave me "$digest already in progress"

Any idea how can I set up my code to make my views refresh on data load? I will be having several different http requests and I would like to know how to set them up properly! I would really appreciate any input!

Here is some of the code I am working with.

app.factory('HttpRequestFactory', function($http, $q) {
  var HttpRequestFactory = {
    async: function(url, params) {
      var deferred = $q.defer();
      $http({
        url: url,
        method: post,
        params: params
      })
        .success(function(data, status, headers, config) {
          deferred.resolve(data);
        })
        .error(function(data, status, headers, config) {
          deferred.reject("An error occurred");
        });
      return deferred.promise;
    }
  };
  return HttpRequestFactory;
});

Factory

function initializeAll(){   
    HttpRequestFactory.async('../api', {action: 'getall'}).then(function(data) {
            //$rootScope.$apply(function () {
                allData = data;
            //});
        angular.forEach(allData, function(value, index){
            console.log('Voala!');
        });
    });
}

Controller calling the factory's function initializeAll()

app.controller("MainController", ["$scope", "$rootScope","MyFactory", 
    function($scope, $rootScope, MyFactory){
        MyFactory.initializeAll();
    
    }
]);
5
  • can you create a jsfiddle? I really doubt whether your code is working or not. At least I can see some syntax error. method: post, will break your code since the post needs to be a string. thanks. Commented Aug 6, 2013 at 20:50
  • hey sza, post is actually a constant that is declared in file and that is how it is working. I will try to get a jsFiddle. Commented Aug 6, 2013 at 20:53
  • Have you tried not to call $apply? The success callback of $http service is wrapped in a call to $apply by Angular itself. Commented Aug 6, 2013 at 21:20
  • Yeah I tried without it. The problem is that I have to click on something to register some action to refresh my view and then all the data appears again. Commented Aug 6, 2013 at 21:27
  • 1
    I would look into using a resolve on the route. As usual, John Lindquist has an excellent video on the topic: youtube.com/watch?v=P6KITGRQujQ Commented Aug 7, 2013 at 3:11

2 Answers 2

3

Oh my !

You got the f** matter with AngularJS !

In fact you have to do a "safeApply" like that for example :

$rootScope.safeApply = function(fn) {
    var phase = this.$root.$$phase;
    if(phase == '$apply' || phase == '$digest') {
        if(fn && (typeof(fn) === 'function')) {
            fn();
        }
    } else {
        this.$apply(fn);
    }
};

In AngularJS you can only have one $apply or $digest loop at the same time.

For details on these loops look at the docs : http://docs.angularjs.org/guide/concepts

It will explain what is the $apply loop and you'll understand a lot of things about the two-way-data-binding in AngularJS

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

3

Don't use $apply: use $watch.

Calling $apply is (almost) always the wrong thing to do. The only time you should ever be calling it is if you've triggered a change outside of an 'angular' method; here, since the trigger occurs in an angular $http request, you can't call $apply because it's already being done at that moment by the $http block. Instead, what you want to do is $watch.

Official Doc for $scope.$watch() here

This will let you watch an object and update whenever it changes. I assume that your view is based on allData and you want it to update immediately; if you're using an ng method, then the watch is automatically setup for you and no more work should be needed. If you're using allData yourself inside a controller, you can write the watch in that controller like this:

$scope.$watch(function thingYouWantToWatch(){
         return <accessor call to allData here>;
      }, 
      function whatToDoOnChange(newValue, oldValue){
          $scope.myCoolThing = newValue; //this is the newValue of allData, or whatever you're watching.
      }
);

1 Comment

The official doc seems to have moved. Is this the equivalent now? docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.