1

All, I have the following AngularJS below. Why is $scope.gotData not visible outside of the call to getData.success()? Note that $scope.gotData is visible to the view: I can display the value of scope.gotData by placing {{gotData}} within my index.html.

Why can I not access $scope.gotData as a variable elsewhere in my controller? This is a question concerning the details of $scope.

getData.js

myApp.factory('getData',function($http){
return $http.jsonp('https://foo.com/bar.json')
       .success(function(data){
            return data;
        })
        .error(function(err){
            return err;
        });
});

MainController.js

myApp.controller('MainController', ['$scope','getData', function($scope, getData){
    getData.success(function(data){
        $scope.gotData = data;
    });

    $scope.gotData /* NOT DEFINED HERE */
   }]);

index.html

<html>
  <head>
    <script src="js/vendor/angular.js"></src>
  </head>
  <body ng-app="MyApp">
    <div ng-controller="MainController">
      {{gotData}} /* I CAN SEE THE DATA HERE */
     </div>
  </body>
</html>
fuzzybear3965
  • 243
  • 5
  • 15

3 Answers3

4

the call to getData.success is asynchronous. So before executing that success function, your call to console of $scope.gotData is done. So you are supposed to define a default value of this $scope.gotData beforehand. And when you get a success call, only then you should use it. Something like this :

myApp.controller('MainController', ['$scope','getData', function($scope,getData){

    $scope.gotData = null;

    getData.success(function(data){
        $scope.gotData = data;
    });

    if($scope.gotData != null) 
        // Do something useful with this data
}]);
fuzzybear3965
  • 243
  • 5
  • 15
binariedMe
  • 4,309
  • 1
  • 18
  • 34
  • 1
    So, does this mean that once the data has been retrieved that ``$scope.gotData`` is then defined? If the server responds fairly quickly then I should be able to ``setTimeout(console.log($scope.gotData),1000)`` outside of the call to ``getData.success`` to see the data, right? – fuzzybear3965 Jul 12 '15 at 19:01
  • 1
    Yes..You'd be able to see the data if you use setTimeout(or `$timeout` service if you want to do thing more angular way). *Provided* you get the data within 1 second(1000 milliseconds) – nalinc Jul 12 '15 at 19:04
1

The reason why you cannot see $scope.gotData outside the getData.success() function is because .success() is async and the value is not available when you try to access it outside .success(). This essentially means that the promise has not been resolved yet.

Moreover, once angular's digest cycle identifies that $scope.gotData is populated, it quickly updates the view. Thats the reason why you can see $scope.gotData in the view

Things would be more clear once you put a watch on $scope.gotData

 myApp.controller('MainController', ['$watch','$scope','getData', function($watch,$scope, getData){
     getData.success(function(data){
        $scope.gotData = data;
     });

    $scope.$watch($scope.gotData,function(n,o){
       console.log("new val-> "+n);
       console.log("old val->"+o);
     })   
 }]);
nalinc
  • 7,375
  • 24
  • 33
  • 1
    So, the promise is only resolved when ``getData.success`` is called from the controller? I want to understand the "digest cycle" and these "promises" ($q). Do you have a good layman's resource for understanding what Angular is doing in the background? – fuzzybear3965 Jul 12 '15 at 19:03
  • Well, yea. When the promise is resolved, `.success` is called, and when it is rejected, `.error` is called. You can get a better idea on promises from this [link](http://andyshora.com/promises-angularjs-explained-as-cartoon.html). Also, [this](http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/) and [this](https://thinkster.io/a-better-way-to-learn-angularjs/promises) are nice places to get started with `$q`. Here's the [official docs](https://docs.angularjs.org/api/ng/service/$q) on $q – nalinc Jul 12 '15 at 19:10
  • I'll read those. I had already found the official documentation but the Angular API can be pretty opaque if you don't have a background in computer science. I'm reading on the ``$watch`` service right now. [This site](http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/) is pretty good. – fuzzybear3965 Jul 12 '15 at 19:35
0

Although not the best programming practice, but a solution to this is to use $rootScope.

myApp.controller('MainController', ['$scope','getData', function($scope, getData){
    getData.success(function(data){
        $rootScope.gotData = data;
    });

    console.log($rootScope.gotData) // this will be accessible across
                                    // different controllers aswell
   }]);