1

I have a button which executes a function with a promise that gets and displays data from firebase in html (I'm using angularJS, ionic and firebase).

The problem is : if I don't inlclude a .then(function(){}) after it, the promise gets executed in unsynchronous way, meaning I have to click the button once again so the data gets displayed in html.

I want to put the data in the scope after the promise (that gets the data from firebase), but for some reason in only works if I put a .then function after it.

However, the data gets displayed normally in the console, but not in html (meaning I think that the function doesn't get attached to the scope).

Here is the piece of code :

$scope.displayChat = function () {
    var userId = firebase.auth().currentUser.uid; // Get ID
    var deferred = $q.defer()

    var db = firebase.database();
    var ref = db.ref("12346787");

  ref.on("value", function(snapshot) {
           console.log(snapshot.val());
           $scope.chatArray = snapshot.val();
           deferred.resolve()

       }, function (errorObject) {
           console.log("The read failed: " + errorObject.code);
       })

   return deferred.promise.then(function(){
        // Removing this empty .then(function(){}) function
        // will result in asynchronousity.
        // just "return deferred.promise;" doesn't work.
    })
}

Any solutions? I don't have much experience with promises but I didn't find anything related. Cheers.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Pokfin
  • 23
  • 3
  • 1
    Why do you need the promise? You are note resolving anything. **deferred.resolve()** --> does not resolve any value. – Hosar Jan 27 '17 at 14:09

3 Answers3

1

the purpose of promise is to manage asynchronous methods, so I don't really understand the problem...

Moreover normally the code inside displayChat must be executed, only the callback must be executed after. You should return the promise, that enable you to execute callback once you are sure the required asynchronous method are done.

PortePoisse
  • 219
  • 2
  • 10
1

When changes to scope are done by events external to the AngularJS framework, the framework needs to do an $apply to initiate a digest cycle to update the DOM.


(source: angularjs.org)

The .then method of a $q service promise automatically initiates the necessary digest cycle. In this case, the promise returned by the displayChat function is discarded and no digest cycle gets initiated. The subsequent click of the button initiates the digest cycle.

In the future, someone might wish to chain from the promise returned by the displayChat function. I recommend making the function more versatile by returning a proper promise and moving any changes to scope into a .then method.

$scope.displayChat = function () {
    var userId = firebase.auth().currentUser.uid; // Get ID
    var deferred = $q.defer()

    var db = firebase.database();
    var ref = db.ref("12346787");

    //ref.on("value", function(snapshot) {
    //USE once
    ref.once("value", function(snapshot) {
           console.log(snapshot.val());
           //$scope.chatArray = snapshot.val();
           deferred.resolve(snapshot.val());

       }, function (errorObject) {
           console.log("The read failed: " + errorObject.code);
           //ADD rejection case
           deferred.reject(errorObject);
       })

   return deferred.promise.then(function(chatArray){
       //Move scope changes here
       $scope.chatArray = chatArray;
       //RETURN to chain data
       return chatArray;

       // Removing this empty .then(function(){}) function
       // will result in asynchronousity.
       // just "return deferred.promise;" doesn't work.
   })
}

Also to avoid memory leaks, use ref.once instead of ref.on and be sure to reject the promise in the error case.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
georgeawg
  • 48,608
  • 13
  • 72
  • 95
0

Promises are used to defer execution of some logic until the promise has been satisfied - ie: you have received your results from the database. In your case, you are already deferring your console display and setting of the $scope variable within ref.on. The promise code is redundant.

The fact that your result shows in the console proves you have received the result. When you update data in the scope, it will not appear until a digest cycle has occurred. Most of the time, Angular can automatically figure out when it needs to run a digest cycle. On those times when it does not, you can force it by wrapping your scope related logic in a timeout, in which case, your code would look as follows:

$scope.displayChat = function () {
var userId = firebase.auth().currentUser.uid; // Get ID

var db = firebase.database();
var ref = db.ref("12346787");

ref.on("value", function(snapshot) {
       console.log(snapshot.val());
       $timeout(function () {
            $scope.chatArray = snapshot.val();
       });

   }, function (errorObject) {
       console.log("The read failed: " + errorObject.code);
   })

}

Your use of the promise .then method just happened to trigger a digest cycle. The promise itself really wasn't doing anything.

If your intention was to pass the snapshot back to the caller when it became available, that's when the promise comes into play, and would be done as follows:

    $scope.displayChat = function () {
    var userId = firebase.auth().currentUser.uid; // Get ID
    var deferred = $q.defer()

    var db = firebase.database();
    var ref = db.ref("12346787");

    ref.on("value", function(snapshot) {
            deferred.resolve(snapshot)

       }, function (errorObject) {
           console.log("The read failed: " + errorObject.code);
       })

   return deferred.promise;
};

$scope.callDisplayChat = function () {
    $scope.displayChat().then(function (result) {
        $scope.chatArray = result.val();
    });
};
grumpyhoser
  • 108
  • 3
  • 6
  • I get it, thank you so much! Since my friend set this up and he didn't know why this happened, I asked it here. I kinda knew / heard about $scope.digest() and $scope.apply(), but didn't really know how they worked, so thanks a bunch :D – Pokfin Jan 27 '17 at 15:18
  • @Pokfin - Anytime! If this solved your problem, please be sure to make it as the accepted answer. Fyi... you can find more about $scope.apply vs $timeout here - http://stackoverflow.com/questions/23070822/angular-scope-apply-vs-timeout-as-a-safe-apply – grumpyhoser Jan 27 '17 at 17:19