Norfeldt Norfeldt - 5 months ago 21
AngularJS Question

AngularJS update Service values when connected or disconnected to firebase

I wish to change the icon color when connected or disconnected to the firebase server. I got this far:

HTML

<button class="button button-icon ion-cloud" ng-style="dbConnectedStyle"></button>


Controller

firebaseRef.$loaded().then( function() {
$scope.dbConnectedStyle = {'color': dbConnectStatus.color};
}


Service

.service('dbConnectStatus', function(firebaseRef){
var status = false;
var color = 'transparent';
var connectedRef = firebaseRef.child(".info/connected");
connectedRef.on("value", function(snap) {
status = snap.val();
if (status) {
color = 'lightgrey';
console.log("Connected to DB (" + color + ")" );
} else {
color = 'transparent';
console.log("Disonnected to DB (" + color + ")" );
}
});
return {
'boolean': status,
'color': color
}
})


It change color the first time. But when disconnecting it doesn't change... seems like it's not two-way binding to the service. How do I achieve this?




UPDATE



Tried to do a reference to the Service as an object rather than doing primitives assignments as explained in the good tutorial A Tale of Frankenstein and Binding to Service Values in Angular.js

I changed the code to the following

HTML

<button class="button button-icon ion-cloud"
ng-style="dbConnectionStatus.connectionStyle">
</button>


Service

.service('dbConnectStatus', function(firebaseRef, $rootScope){
this.status = false;
var styles = {
'offlineStyle': {'color': 'red'},
'onlineStyle': {'color': 'lightgrey'}
};
this.connectionStyle = styles.offlineStyle;

firebaseRef.child(".info/connected")
.on("value",
function(snap) {
this.status = snap.val();
if (snap.val()) {
console.log("Connected to DB.");
this.connectionStyle = styles.onlineStyle;
console.log(this.connectionStyle);
} else {
console.log("Disconnected to DB.");
this.connectionStyle = styles.offlineStyle;
console.log(this.connectionStyle);
}
console.log(this.status);
$rootScope.$broadcast('dbConnection:changed');
}
);

})


Controller

$scope.dbConnectionStatus = dbConnectStatus;
$scope.$on('dbConnection:changed', function() {
console.log("'on(...)' called. This is the $scope.dbConnectionStatus.connectionStyle:");
$scope.dbConnectionStatus = dbConnectStatus;
console.log($scope.dbConnectionStatus.connectionStyle);
console.log("This is the dbConnectStatus.connectionStyle:");
console.log(dbConnectStatus.connectionStyle);
});
$rootScope.$watch('dbConnectStatus', function (){
$scope.dbConnectionStatus = dbConnectStatus;
});
//$rootScope.$apply();


I then reloaded the code and got this console message

First time load

I then turned off the connection

Internet connection off

I then turn on the connection

Internet connection on

It is clear to me that the service
dbConnectionStatus
isn't updated as a global variable in the way that I expected. I was on the assumption that a service is called once when the application is load and that assigning a scope variable to a service (object) is not a call but a reference...

What am I doing wrong?

Answer

I worked in a jsFiddle using $emit and $on to handle the status changes inside the service. The main problem is that when going online the angular binding was not working properly so I needed to force an angular cycle with $scope.$apply().

I started working on the first version of your code but made some refactoring. You can find the full code on the jsFiddle but the service and the controller look like the following:

Service

.service('dbConnectStatus', function($rootScope){
  var status = false;
  var color = 'red';
  var self = {      
        startWatchingConnectionStatus: function(){
        var connectedRef = firebase.database().ref().child(".info/connected");
        connectedRef.on("value", function(snap) {
            console.log(snap.val());
            status = snap.val();
            if (status) {
                color = 'blue';
                console.log("Connected to DB (" + color + ")" );
            } else {
                color = 'red';
              console.log("Disonnected to DB (" + color + ")" );
              }
            $rootScope.$emit('connectionStatus:change', {style: {'color': color}, status: status}});
        });
      },
      getStatus: function(){
        return status;
      },
      getColor: function(){
        return color;
      }
  };
  return self;
})

Controller

.controller('HomeCtrl', ['$scope', 'dbConnectStatus', '$rootScope',function($scope, dbConnectStatus, $rootScope) {

    dbConnectStatus.startWatchingConnectionStatus();

    $rootScope.$on('connectionStatus:change',  function currentCityChanged(event, value){
        $scope.color = value.style;
        //if changed to connected then force the $apply
        if(value.status === true){
            $scope.$apply();
        }
    });  
}]);

Let me know if there is anything that is still not clear.