AngularJS update Service values when connected or disconnected to firebase
Asked Answered
I

3

10

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?

Ivette answered 22/6, 2016 at 20:23 Comment(5)
Nothing to do with angular or firebase here. This is just how passing by value works in javascript. dbConnectedStyle does not reference dbConnectStatus's color variable. It just copies the valueOrnithischian
How would you pass the variable reference and not the value?Ivette
store the color in an object on the service. e.g. var style = {color: 'transparent'}. And reference the object on your controllerOrnithischian
Isn't that already what I'm doing?Ivette
@Ornithischian tried to do your suggestion, but as you can see in the updated question I failed...Ivette
S
5

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.

Stomy answered 25/6, 2016 at 1:29 Comment(1)
Your suggestion of passing the style in the broadcast got me to a solution that works. Thank you so much!Ivette
I
0

Inspired from @adolfosrs great answer I found the following solution to work for me.

Service

.service('dbConnectStatus', function(firebaseRef, $rootScope){
  // Initial setup
  var styles = {
    'offlineStyle': {'color': 'red'},
    'onlineStyle': {'color': 'skyeblue'}
  };

  // Functions to switch status
  var offline = function () {
    this.boolean = false;
    this.style = styles.offlineStyle;
  }
  var online = function () {
    this.boolean = true;
    this.style = styles.onlineStyle;
  }

  var get_status = function(){
    return {
      boolean: this.boolean,
      style: this.style
    }
  }

  // Read the firebase info and update when changed
  firebaseRef.child(".info/connected")
    .on("value", function(snap) {
        if (snap.val()) {
          online();
        } else {
          offline();
        }
        $rootScope.$emit('dbConnection:changed', get_status() );
    });
})

Controller

// Hide it before the status is known.
$scope.dbConnectionStatus = {'color': 'transparent'}; 
// Getting and reading status changes
$rootScope.$on('dbConnection:changed', function(event, status) {
    $scope.dbConnectionStatus = status.style;
    $scope.$apply();
  });
Ivette answered 25/6, 2016 at 9:36 Comment(0)
O
0

You should be able to get this working simply by storing the color in an object in the service and referencing it from the controller. e.g.

View

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

Controller

$scope.dbStatusService = dbConnectStatus;

Service

.service('dbConnectStatus', function(firebaseRef){
    var status = false;
    var style = {color: 'transparent'};
    var connectedRef = firebaseRef.child(".info/connected");
    connectedRef.on("value", function(snap) {
        status = snap.val();
        if (status) {
            style.color = 'lightgrey';
        } else {
            style.color = 'transparent';
        }
    });
    return {
        'boolean': status,
        'style': style
    }
});
Ornithischian answered 25/6, 2016 at 17:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.