0

Here is the code: I'm looping through the data to get the value that I need, and I got it. The questions is how can I access the value outside of this loop in other functions within the controller. The correct value have been assigned to $scope.theModel.

var refModels = firebase.database().ref("/users/" + $scope.UserID);

refModels.once('value').then(function (snapshot) {
  snapshot.forEach(function (snapshot) {
    var usersValue = snapshot.val();
    console.log("users values", usersValue.carModel);

    $scope.theModel = usersValue.carModel;
    console.log("The model" + $scope.theModel); //output "e90"


  });
      $scope.processData();
});

So I need to use that value $scope.theModel outside of the function. What I'm trying to achieve, I will use the value of $scope.theModel -"e90" and compare it with another DB ref, and if it matches it will return the data. See the code of another DB ref:

 $scope.processData = function (){

console.log("Process Model" + $scope.theModel); // returns undefined


$scope.carDetails = [];

firebase.database().ref("models").once('value').then(function(snapshot) {
    snapshot.forEach(function(userSnapshot) {
        var models = userSnapshot.val();
        console.log(models);


        if (models.brand == $scope.theModel){

           $scope.carDetails.push(models);
       }
   });
});

};

AlexFF1
  • 1,233
  • 4
  • 22
  • 43

4 Answers4

2

It is because, refModels.once('value').then is async meaning that JS starts its execution and continues to next line which is console.log and by the time console.log is executed $scope.theModel hasn't been populated with data yet.

I suggest you read this Asynchronous vs synchronous execution, what does it really mean?

You can still access your $scope.theModel in other functions, but you have to make sure it is loaded first.

Edit

refModels.once('value').then(function (snapshot) {
    snapshot.forEach(function (snapshot) {
         var usersValue = snapshot.val();
         console.log("users values", usersValue.carModel);

         $scope.theModel = usersValue.carModel;
         console.log("The model" + $scope.theModel); //output "e90"

    });
    $scope.processData();
});

$scope.processData = function() {
// here $scope.theModel exists
// do some code execution here with $scope.theModel
// call your other firebase.database.ref("models") here
};
Bunyamin Coskuner
  • 8,719
  • 1
  • 28
  • 48
  • thanks for info, you know how to do this in code? really appreciate – AlexFF1 Jun 27 '17 at 09:31
  • Easiest and safest way to do this, call a function from inside of then function. This way, you'll know for sure you have the data. – Bunyamin Coskuner Jun 27 '17 at 09:43
  • hi thanks, trying to implement your solution, but can't seem to get it working. Can you explain it in code please, appreciate – AlexFF1 Jun 27 '17 at 10:33
  • Can you explain what you need to do exactly? Do you need to show some data from Firebase in your template? – Bunyamin Coskuner Jun 27 '17 at 10:43
  • $scope.theModel returns udefined (.. $scope.processData = function (){ console.log("Process Model" + $scope.theModel); $scope.carDetails = []; firebase.database().ref("models").once('value').then(function(snapshot) { snapshot.forEach(function(userSnapshot) { var models = userSnapshot.val(); console.log(models); if (models.brand == $scope.theModel){ $scope.carDetails.push(models); } }); }); }; – AlexFF1 Jun 27 '17 at 11:09
  • It may be that your snapshot has a null value. Also which one of the console log gets printed out first? "The model e90" or "Process Model undefined" – Bunyamin Coskuner Jun 27 '17 at 11:28
  • the model e90 prints out first, but "Process Model undefined" – AlexFF1 Jun 27 '17 at 15:25
1

Ok so finally got it working, with Bunyamin Coskuner solution, the only thing I had to fix is to return $scope.processData;

  var UserID = firebase.auth().currentUser.uid;

    var refModels = firebase.database().ref("/users/" +  UserID);




   refModels.once('value').then(function(snapshot) {

    console.log(snapshot)

           snapshot.forEach(function(childSnapshot) {

    console.log(childSnapshot)

           var usersValue = childSnapshot.val();

         // var theModel = usersValue.carModel;
          console.log("The model", usersValue.carModel); //output "e90"

         $scope.theModel = usersValue.carModel;
         ***return $scope.processData;*** // we have to return it here
    });

 $scope.processData();
});



$scope.processData = function (){

console.log("Process Model" + $scope.theModel); // now returns the value e90


$scope.carDetails = [];

firebase.database().ref("models").once('value').then(function(snapshot) {
  snapshot.forEach(function(userSnapshot) {
    var models = userSnapshot.val();
    console.log(models);


     if (models.brand == $scope.theModel){

    $scope.carDetails.push(models);
         }
       });
    });

   }

It seems to be a very common mistake made by new javascript developers, the Async behaviour, so its good to read about it.

In this solution we called a function from inside of then function. That way, we'll have for sure the data, that can be used in other function

AlexFF1
  • 1,233
  • 4
  • 22
  • 43
0

Your $scope.theModel gets set inside the "then" block as expected, but being async, this happens after the second console.log (outside the function) is invoked.

If you use angular components, you can init theModel inside $onInit method:

angular.module('app', []).component('myComponent', {
   bindings: {
     title: '@'
   },
   controller: function() {
      this.$onInit = function() {

        // Init the variable here
        $scope.theModel = null;        
      });

      //Following code should not be in the controller, but in a service (here just as sample)
      var refModels = firebase.database().ref("/users/" + $scope.UserID);

      refModels.once('value').then(function (snapshot) {
        snapshot.forEach(function (snapshot) {
          var usersValue = snapshot.val();
          console.log("users values", usersValue.carModel);

          // From this moment your variable is set and available outside
          $scope.theModel = usersValue.carModel;
      });
   },
   templateUrl: 'template.html'
   });
Francesco
  • 9,947
  • 7
  • 67
  • 110
0

You can watch the value changing. Async behaviour is explained in another answer.

   $scope.$watch('theModel', function(newVal){
      console.log(newVal);
   });

Also I would use $evalAsync() on $scope.theModel = usersValue.carModel;

$scope.$evalAsync(function(){
    $scope.theModel = usersValue.carModel;
});
Gray Fox
  • 357
  • 3
  • 10