0

I have a Firebase function inside an angular controller. There is a button that when clicked takes the selected option and the type input and stores them into the user's object like so:

{
    selected-option : input-value
}

This works perfectly, but only works when the view is changed. In my case both airlines already have data so this function displays an $ionicPopup. After the view has changed once the functionality is absolutely perfect. This is obviously a problem and I assume it is an $apply or $digest issue.

Here is my controller code (Supected location marked by "ISSUE RIGHT HERE"):

.controller('ProgramCtrl', ['$scope', '$state', '$firebaseArray', 'facebook', '$ionicPopup', '$ionicLoading',
function($scope, $state, $firebaseArray, facebook, $ionicPopup, $ionicLoading) {
$scope.goBack = function() {
    $state.go('app.home');
}

$scope.show = function() {
    $ionicLoading.show({
      template: 'Loading...'
    });
};
$scope.hide = function(){
    $ionicLoading.hide();
};

// Get who is logged in
$scope.user = facebook.get();

// Array of airlines
var airRef = ref.child("airlines");
$scope.airlines = $firebaseArray(airRef);
$scope.selectedAir = {};
$scope.miles = {};

$scope.revealInput = function(num) {
  // Jquery variables
  $milesPoints = $(".milesPoints");
  $saveTicket = $(".saveTicket");
  // Will fade in visibility depending on num passed into function
  switch(num) {
      case 1:
          $saveTicket.prop("disabled", false);
          $saveTicket.fadeTo(400, 1);
          break;
      default:
          break;
  }
}

// Add program to user
$scope.addProgram = function () {
  // Connect to Firebase
  Firebase.goOnline();
  // Check for facebook user
  if(jQuery.isEmptyObject($scope.user)) {
      // Get Firebase user
      var authData = ref.getAuth();
      var theUser = ref.child("users").child(authData.uid);
      var selected = {};
      // Access user miles data
      // $scope.show();
      // ISSUE RIGHT HERE    
     theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
          // Update scopes
              var exist = snapshot.exists();
              // Check if object id exists, if so notify user
              if(!exist) {
                  // Save and update program to user object
                  selected[$scope.selectedAir.name.$id] = $scope.miles.num;
                  theUser.child("miles").update(selected);
                  //$scope.hide();
                  $state.go("app.saved");
              } else {
                  // Popup alert
                  var alertPopup = $ionicPopup.alert({
                    title: 'Oops!',
                    template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
                  });
                  alertPopup.then(function(res) {
                    console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
                  });
              }
      })

  } else {
      var theUser = ref.child("users").child($scope.user.id);
      var selected = {};
      $scope.show();
      theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
          var exist = snapshot.exists();
          if(!exist) {
              selected[$scope.selectedAir.name.$id] = $scope.miles.num;
              theUser.child("miles").update(selected);
              $scope.hide();
              $state.go("app.saved");
          } else {
              var alertPopup = $ionicPopup.alert({
                title: 'Oops!',
                template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
              });
              alertPopup.then(function(res) {
                console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
              });
          }
      })

    }
  }
  }])

Thanks for the help and I can provide more code or screenshots if needed.

theblindprophet
  • 7,767
  • 5
  • 37
  • 55

1 Answers1

2

The issue is in this piece of code:

theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
    $timout(function() {
          var exist = snapshot.exists();
          // Check if object id exists, if so notify user
          if(!exist) {
              // Save and update program to user object
              selected[$scope.selectedAir.name.$id] = $scope.miles.num;
              theUser.child("miles").update(selected);
              //$scope.hide();
              $state.go("app.saved");
          } else {
              // Popup alert
              var alertPopup = $ionicPopup.alert({
                title: 'Oops!',
                template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
              });
              alertPopup.then(function(res) {
                console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
              });
          }
    });
})

When you call once(), it starts loading data from Firebase. Since this may take some time, you pass in a callback function that is invoked when the data is available. But by the time the callback function is invoked, AngularJS is not expecting updates to the $scope anymore.

The solution is to wrap the code into a $timeout(), which ensures it gets executed when AngularJS is ready to handle scope changes again:

 theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
      // Update scopes
          var exist = snapshot.exists();
          // Check if object id exists, if so notify user
          if(!exist) {
              // Save and update program to user object
              selected[$scope.selectedAir.name.$id] = $scope.miles.num;
              theUser.child("miles").update(selected);
              //$scope.hide();
              $state.go("app.saved");
          } else {
              // Popup alert
              var alertPopup = $ionicPopup.alert({
                title: 'Oops!',
                template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
              });
              alertPopup.then(function(res) {
                console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
              });
          }
  })

Note that this problem wouldn't happen if you used AngularFire's $firebaseObject() and $firebaseArray() primitives, since those automatically notify AngularJS of scope changes.

We get this question a lot. Here's a recent one: Taking long to load

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807