0
votes

Update: per Kato's reply, I created example codes. see below.


Simply copying the code at https://www.firebase.com/docs/web/libraries/angular/guide.html#section-angular-authentication results in a working codepen (here it is as a github gist) that logs me in/out, yet does not "refresh" the data, i.e. the expression {{ user.facebook.displayName }} is not really bound.

The question: Is there a way to make the controller actually bind to the logged-in state?

Note I: there is no direct link to the code on firebase.com, is the code that appears under the sentence

Pulling some of these concepts together, we can create a login form with dynamic content based on the user's authentication state

Note II: in order to make the copied code work, you have to replace the firebase url with your own.

edit - just discovered the "code snippet" feature:

var app = angular.module("sampleApp", ["firebase"]);

// let's create a re-usable factory that generates the $firebaseAuth instance
app.factory("Auth", ["$firebaseAuth", function($firebaseAuth) {
  var ref = new Firebase("https://<your-firebase>.firebaseio.com/");
  return $firebaseAuth(ref);
}]);

// and use it in our controller
app.controller("SampleCtrl", ["$scope", "Auth", function($scope, Auth) {
  $scope.auth = Auth;
  $scope.user = $scope.auth.$getAuth();
}])
<html ng-app="sampleApp">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
    <script src="https://cdn.firebase.com/js/client/2.0.4/firebase.js"></script>
    <script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>
    <script src="app.js"></script>
  </head>
  <body ng-controller="SampleCtrl">
    <div ng-show="user">
      <p>Hello, {{ user.facebook.displayName }}</p>
      <button ng-click="auth.$unauth()">Logout</button>
    </div>
    <div ng-hide="user">
      <p>Welcome, please log in.</p>
      <button ng-click="auth.$authWithOAuthPopup('facebook')">Login</button>
    </div>
  </body>
</html>
2
Please edit your question to include the relevant code. It's great that you took the effort to set up a codepen (I'll head over there next), but that is not a replacement for having the relevant code in your question. - Frank van Puffelen
@FrankvanPuffelen, the code is simply a bit lengthy - isn't the Github gist good enough? - H.Wolper
I inserted the code as a snippet (and thank you) - H.Wolper

2 Answers

2
votes

$getAuth() is a synchronous function call, as documented and explained. You need to utilize $onAuth() if you want it to update asynchronously each time the login state changes.

Auth.$onAuth(function(userData) {
   // $timeout needed for AngularFire 0.9.0
   // see https://github.com/firebase/angularfire/issues/510
   $timeout(function() {
      $scope.user = userData;
   });
});

But really, you should be using routes and the resolve method, so you don't have to deal with all this auth stuff in the scope and controllers, as explained in the docs just below the section you are reading now.

0
votes

Per @Kato's answer, I created 2 examples (with snippets and codepens), for anyone who might get stuck there, as I did.

The first, simply shows that using $onAuth as shown by @Kato, will simulate the binding. The second, is the same but also with ng-view to show usage with routes, per @Kato's advice.

First snippet: (codepen here)

var app = angular.module("sampleApp", ["firebase"]);

// let's create a re-usable factory that generates the $firebaseAuth instance
app.factory("Auth", ["$firebaseAuth",
  function($firebaseAuth) {
    var ref = new Firebase("https://glowing-inferno-4287.firebaseio.com/");
    //var ref = new Firebase("https://<replace-with-your-db-id>.firebaseio.com/");
    return $firebaseAuth(ref);
  }
]);

// and use it in our controller
app.controller("SampleCtrl", ["$scope", "Auth", "$timeout",
  function($scope, Auth, $timeout) {
    $scope.auth = Auth;
    $scope.auth.$onAuth(function(userData) {
      // $timeout needed for AngularFire 0.9.0
      // see https://github.com/firebase/angularfire/issues/510
      $timeout(function() {
        $scope.user = userData;
      });
    });
  }
]);
<html ng-app="sampleApp">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
  <script src="https://cdn.firebase.com/js/client/2.0.6/firebase.js"></script>
  <script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>

</head>

<body ng-controller="SampleCtrl">
  <div ng-show="user">
    <p>Hello, {{ user.facebook.displayName }}</p>
    <button ng-click="auth.$unauth()">Logout</button>
  </div>
  <div ng-hide="user">
    <p>Welcome, please log in.</p>
    <button ng-click="auth.$authWithOAuthPopup('facebook')">Login</button>
  </div>
</body>

</html>

Second snippet: (codepen here)

var app = angular.module("sampleApp", ["firebase", "ngRoute"]);
// let's create a re-usable factory that generates the $firebaseAuth instance
app.factory("Auth", ["$firebaseAuth",
  function($firebaseAuth) {
    var ref = new Firebase("https://<replace-with-your-db-id>.firebaseio.com/");
    return $firebaseAuth(ref);
  }
]);

// and use it in our controller
app.controller("SampleCtrl", ["$scope", "Auth", "$timeout", "$location", "$route",
  function($scope, Auth, $timeout, $location, $route) {
    $scope.auth = Auth;
    $scope.auth.$onAuth(function(userData) {
      // $timeout needed for AngularFire 0.9.0
      // see https://github.com/firebase/angularfire/issues/510
      $timeout(function() {
        $scope.user = userData;
      });
      $route.reload();
    });

    $scope.login = function() {
      $scope.auth.$authWithOAuthPopup('facebook')
        .then(function() {
          $location.path("/account");
        });
    }
  }
]);

app.controller("AccountCtrl", ["Auth",
  function(Auth) {
    this.userData = Auth.$getAuth();
  }
]);

app.run(["$rootScope", "$location",
  function($rootScope, $location) {
    $rootScope.$on("$routeChangeError", function(event, next, previous, error) {
      // We can catch the error thrown when the $requireAuth promise is rejected
      // and redirect the user back to the home page
      if (error === "AUTH_REQUIRED") {
        $location.path("/");
      }
    });
  }
]);

app.config(["$routeProvider",
  function($routeProvider) {
    $routeProvider.when("/", {
      // the rest is the same for ui-router and ngRoute...
      templateUrl: "views/home.html",
      resolve: {
        // controller will not be loaded until $waitForAuth resolves
        // Auth refers to our $firebaseAuth wrapper in the example above
        "currentAuth": ["Auth",
          function(Auth) {
            // $waitForAuth returns a promise so the resolve waits for it to complete
            return Auth.$waitForAuth();
          }
        ]
      }
    }).when("/account", {
      // the rest is the same for ui-router and ngRoute...
      templateUrl: "views/account.html",
      resolve: {
        // controller will not be loaded until $requireAuth resolves
        // Auth refers to our $firebaseAuth wrapper in the example above
        "currentAuth": ["Auth",
          function(Auth) {
            // $requireAuth returns a promise so the resolve waits for it to complete
            // If the promise is rejected, it will throw a $stateChangeError (see above)
            return Auth.$requireAuth();
          }
        ]
      }
    });
  }
]);
<html ng-app="sampleApp">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
  <script src="https://cdn.firebase.com/js/client/2.0.6/firebase.js"></script>
  <script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-route.min.js"></script>
</head>

<body ng-controller="SampleCtrl">
  <div ng-show="user">
    <p>Hello, {{ user.facebook.displayName }}</p>
    <button ng-click="auth.$unauth()">Logout</button>
  </div>
  <div ng-hide="user">
    <p>Welcome, please log in.</p>
    <button ng-click="login()">Login</button>
  </div>
  <hr />
  <div ng-view></div>
  <script type="text/ng-template" id="views/home.html">
    home template.
  </script>
  <script type="text/ng-template" id="views/account.html">
    <div ng-controller="AccountCtrl as accntCtrl">
      account template.
      <br />Hello from account page, {{accntCtrl.userData.facebook.displayName}}
    </div>
  </script>
</body>

</html>