4

I have a service in AngularJs which will return a value from Database.

userData.getUserData(function(response) {
  $scope.uid = response
});

when I inject this service in my controller it will return a Promise, But i need this Promise value Outside of my Function, how can i do that ?

Plunkr link of code

Aamir shah
  • 141
  • 3
  • 10

3 Answers3

3

From your plunker code you have a service which looks like this:

angular.module('plunker');
.service('myService', function($firebaseRef, $firebaseObject){

    this.getUserData = function(el) {
            $firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
                el(data);
            })
        }
});

and a controller like this:

app.controller('MainCtrl', function($scope, myService) {

 myService.getUserData(function(response) {  
   $scope.uid = response;
 })

 console.log($scope.uid);  

 $scope.postRequest = function(val) {
            $firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
            console.log(val)
            console.log($scope.request);
        }
});

The problem is that the line console.log($scope.uid); prints undefined.

You are thinking in the terms of a standard blocking programming, but in this case the call to getUserData is non-blocking which means that you don't wait for the response, instead you only send the request to the server (Firebase) and continue to the next statement which is console.log.

The callback function(response) { $scope.uid = response; } will be invoked when the client reads success response (HTTP 2xx) returned by the server. This takes at least the time request travels to the server and response to travel back + the time it takes for the server to actually get the data. For example 150ms.

So, basically at the time the console.log statement is executed, the response callback was still not invoked, ie. the $scope.uid is not set which means that the console.log will print undefined.

To resolve this you need to execute your code, which depends on the response from the server, in the callback itself. For example something like this:

app.controller('MainCtrl', function($scope, myService) {

  myService.getUserData(function(response) {  
    $scope.uid = response;
    console.log($scope.uid);
    // and any other code which depends on the $scope.uid
  });

  // ...

});

The cool factor would be to use AngularJS promises via $q service. For example, you could redefine your service like this:

angular.module('plunker');
.service('myService', function($q, $firebaseRef, $firebaseObject){
    var deferred = $q.defer();

    this.getUserData = function(el) {
            $firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
              deferred.resolve(data);
            });
    };

    return deferred.promise;
});

then in your controller you can use your service method like this:

app.controller('MainCtrl', function($scope, myService) {

 myService.getUserData()
   .then(function(data) {
     $scope.uid = data;
     console.log($scope.uid);
     // and any other code 
     // you can also return promises here and then chain
     // them, read about AngularJS promises
   });

   // ...
});

This is basically same as the example before, but with added benefit of better readability which is accomplished by avoiding callback hell.

I noticed that you have postRequest function which uses $scope.uid. I guess that you do not want to execute this function if you do not have $scope.uid. I also guess that this function is called by some event, like click on a button. My recommendation is that you disable the button or whatever else invokes this function until the $scope.uid is loaded. For example like this:

<button type="button" ng-click="postRequest(something)" ng-disabled="uid === undefined">Post</button>

Hope this helps.

stjepano
  • 1,052
  • 7
  • 15
1

You issue that has been discussed has to do with the fact that you are trying to use $scope.uid before your promise has returned anything.

You can get around things like this by taking a few steps, mainly, you can init the scope var before you use. For instance if the response is an object you could just do something like this:

$scope.uid = {};

userData.getUserData(function(response) {
  $scope.uid = response;
});

Then your var wont be undefined. But you should also consider when and how you are using this variable, that will effect if you want to init like this or not.

If you log like this, it will work

userData.getUserData(function(response) {
  $scope.uid = response;
  console.log($scope.uid);
});

and if you log like this it will not work because this log is not going to wait for you promise to return before logging;

userData.getUserData(function(response) {
  $scope.uid = response;
});

console.log($scope.uid);

You'd need to provide more information to determine how best to deal with using this returned information and local variable. But the general idea of the problem is that you are attempting to log the variable before the promise is back.

TL:DR You have access to $scope.uid outside of the function, you need to wait for the reponse to give it the data before it will be inside, you can init it if you do not want it to start out as undefined

UPDATE : you need to use a callback to fire the second call After you have the first call back

 userData.getUserData(function(response) {
  $scope.postRequest(response);
 });
 $scope.postRequest = function(val) {
    $firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
    console.log(val) console.log($scope.request);
 }

Your plunk fixed : https://plnkr.co/edit/KbVoni3jfnHm54M80kYl?p=preview

ajmajmajma
  • 13,712
  • 24
  • 79
  • 133
  • [here is the plunk of complete code](https://plnkr.co/edit/vzcEnK5sYQk6XK1MS2T8?p=preview) – Aamir shah Apr 12 '16 at 13:49
  • @Aamirshah I'm not sure how else to explain this, maybe a better question is what are you trying to do with the data? You can't log it outside and below that, it won't work because you have not init the scope variable and also the promise has not returned the data. Your code just has a console log which doesn't work like that for explained reasons. Where is the actual problem? Javascript is async. – ajmajmajma Apr 12 '16 at 13:52
  • actually i'm trying to send this response to database within another object, – Aamir shah Apr 12 '16 at 13:54
  • @Aamirshah Ok, so use the callback to do that? – ajmajmajma Apr 12 '16 at 13:54
  • please see the updated [plunk](https://plnkr.co/edit/vzcEnK5sYQk6XK1MS2T8?p=preview) – Aamir shah Apr 12 '16 at 13:57
  • @Aamirshah see updated answer, you need to use callbacks – ajmajmajma Apr 12 '16 at 13:59
-1

You have to wait until the process of getting the response from userData.getUserData is done.

There are 3 ways as far as I know to solve this:

Using Callback

function getUserData(callback){
  userData.getUserData(function(response) {
    callback(response);
  });
}

then you call that function

getUserData(function(response){
  $scope.uid = response;
  // then you can proceed and use the $scope.uid here
});

Wrap it in function

getUserData(function(response){
  callAnotherFunction(response);
});

function callAnotherFunction(response){
  console.log(response);
  // You can use the value inside this function
}

Or use timeout

You can use $timeout to give time to the request and assign it to $scope.uid

ariezona
  • 176
  • 3
  • 13