user3344591
user3344591

Reputation: 567

$scope variable of a controller is undefined when returning values from a service

AngularJS newbie here. I'm trying to use a service to get result from Google endpoint. I have initiated Google API and the execute() returns the proper result. But when I try to return this result to a $scope variable in a controller, the $scope variable remains undefined.

The function in the service looks like this:

app.service('gapiService',function(){



var getSubjectList=function(){
            gapi.client.subjectendpoint.listSubject().execute(function(resp){
        if(!resp.code){
            console.log("resp "+resp.items); //this prints the proper result
            return resp.items;


        }
    });
};


return{
    getSubjectList:getSubjectList
};
});

The function in the controller which is invoking the function in the service looks like this :

var getSubjectList=function(){
    $scope.subj=gapiService.getSubjectList();
    console.log($scope.subj); //this is undefined
}

Also, the console.log() in the controller gets printed before the console.log() in the service. So I tried to use promise and changed the service like this :

var getSubjectList=function(){
  var p=$q.defer();
            gapi.client.subjectendpoint.listSubject().execute(function(resp){
        if(!resp.code){
            console.log("resp "+resp.items);
            return resp.items;


        }
    });
   return p.promise;
};

Even with this I'm not getting resp.items into my $scope.subj. I'm sure that I'm using the promise in a wrong way. Or is there any other problem?

Upvotes: 0

Views: 926

Answers (3)

Younes
Younes

Reputation: 309

That's absolutely natural. Promises are just a way to make javascript asynchronous code look nicer ;) so even by using them you still have to respect async logic.

Let me explain:

When you call getSubjectList in your controller, it will execute gapiService.getSubjectList() which will call gapi.client.subjectendpoint.listSubject().execute() but notice that the function where you are logging to the console in your service is a callback so it will be called later, after your current controller's code is finished.

That's the reason why the controller continues to execute and logs before your service's callback.

Another point is that when you use promises, you have to resolve or reject (if something fails) them and your service's code should look like this:

var getSubjectList = function () {

 var deferred = $q.defer();

 gapi.client.subjectendpoint.listSubject().execute(function(resp){
   if(!resp.code){
     deferred.resolve(resp.items);
   }
   else {
     deferred.reject("OH MY GOD! SOMETHING WENT WRONG!")
   }
 });

 return deferred.promise;

};

and your controller's code should be:

var getSubjectList=function(){
    gapiService.getSubjectList().then(function (items) {
      $scope.subj = items;
    });
  }

As you can see, now it's async, as using then, you are waiting for the promise to be resolved in order to call callback that will set the value into the scope.

By the way, AngularJS can bind "promises" and not only value.

So, you can use:

$scope.subj = gapiService.getSubjectList();

If you try to console.log($scope.subj), it will only show you the promise which doesn't contain the value yet but if you use it in your template like this:

<ul ng-repeat="item in $scope.subj">
    <li ng-bind="item"></li>
</ul>

Angular will watch the promise until it's resolved then use the resolved value.

Don't forget to handle errors and things like that using promise.catch and promise.finally.

For performance's sake, use AngularJS 1.3's one-time-binding if your values are not be changed once set.

<ul ng-repeat="item in ::$scope.subj">
    <li ng-bind="::item"></li>
</ul>

We wrote an article about this on our blog www.blog.wishtack.com

Upvotes: 0

Eyal
Eyal

Reputation: 532

You are indeed using promise incorrectly. The API function returns with a callback, and you need to resolve the promise when the callback is called. It should be done in the following manner:

var getSubjectList=function(){
  var p=$q.defer();
  gapi.client.subjectendpoint.listSubject().execute(function(resp){
      if(!resp.code){
          console.log("resp "+resp.items);
          p.resolve(resp.items);


        }
        p.reject();
    });
   return p.promise;
};

You use the "then" function from the promise to get the resolved promise:

var getSubjectList=function(){
    gapiService.getSubjectList().then(function(result){
        $scope.subj=result;
        console.log($scope.subj);
    });

}

Upvotes: 0

Jana
Jana

Reputation: 141

You have to call resolve and reject on p.

var getSubjectList=function(){
  var p=$q.defer();
            gapi.client.subjectendpoint.listSubject().execute(function(resp){
        if(!resp.code){
            console.log("resp "+resp.items);
            p.resolve(resp.items);

        }
       p.reject('error');
    });
   return p.promise;
};

Try this...

Upvotes: 0

Related Questions