AngularFire - Wait until user data is retrieved in controller

angularfire, angularjs, firebase, firebasesimplelogin, javascript

Solution

Try resolving the route for your users. Take the plunker demo below for example.

Plunker Demo

The app is just a simple login page to a profile page. It has a `HomeCtrl` that navigates to a `ResolveCtrl` (which is the profile page) after a login.

The `HomeCtrl` just manages a login view. When a user is successfully logged in we navigate to the profile page. (The Auth object is a custom wrapper I made available in the Plunker).

.controller('HomeCtrl', function($scope, Auth, $window) {
  $scope.user = {
    email: 'bob@email.com',
    password: 'password'
  };
  $scope.login = function() {
    Auth.login('password', $scope.user);
  };
  
  Auth.onLogin(function(e, user) {
    console.log('logged in!');
    $window.location.href = '#/resolve';
  });
  
})

Now that we're logged in we'll go the `ResolveCtrl`. This controller resolves the current user before it has been loaded. That's the `data` parameter.

.controller('ResolveCtrl', function($scope, $route, data) {
  $scope.user = data;
})

But where is that parameter coming from? We can set a resolve object in our $routeProvider. This tells the `ResolveCtrl` it can't load until we've resolved this promise. Since the Auth.getCurrentUser() returns a promise we just need to return that and it will get resolved for us.

.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: '_home.html',
        controller: 'HomeCtrl',
      })
      .when('/resolve', {
        templateUrl: '_resolve.html',
        controller: 'ResolveCtrl',
        resolve: {
          data: function(Auth) {
           // load the user before the view is loaded
           return Auth.getCurrentUser();
          }
        }
      })
      .otherwise({
        redirectTo: '/'
      });
  }
])

Now when the page gets loaded we know that the user will be there.

Problem

The user registers with username, email, and password. I authenticate using FirebaseSimpleLogin with the email, take the user id, and then load my user using the user id: ``` var ref = new Firebase(FIREBASE_URL + 'users'); // data containing user information var users = $firebase(ref); $rootScope.$on('$firebaseSimpleLogin:login', function (e, authUser) { // wait until authenticated var query = $firebase(ref.startAt(authUser.uid).endAt(authUser.uid)); // find user by uid query.$on('loaded', function () { setCurrentUser(query.$getIndex()[0]); // get username from query }); }); function setCurrentUser (username) { $rootScope.currentUser = User.findByUsername(username); } ``` Now, my problem is that I would like to redirect to the user profile page if I am logged in in my HomeCtrl: ``` app.controller('HomeCtrl', function ($rootScope, $scope, $location, Auth, User) { // wait for current user to be set // if signed in // location.path('/user/' + currentUser.username); }); ``` Unfortunately, I am not sure how to wait until the setCurrentUser function is completed before I redirect. Is there a way I can have a function in my controller wait until this other function is completed? I have a workaround, but I'm pretty sure this is not the right way to do it. Right now, I am watching for my user variable to be updated, and then calling all functions after it changes to a user, as in: ``` $scope.$watch( function() { return $rootScope.currentUser }, // watch for variable to change function() { if ($rootScope.currentUser) { // if the current user is defined, change path $location.path('/users/' + $rootScope.currentUser.username); } } ) ```

Original source