2

I have a firebase Auth factory as follows

app.factory("Auth", ["$firebaseAuth", "FIREBASE_URL","$ionicPlatform",
  function($firebaseAuth, FIREBASE_URL, $ionicPlatform) {
      var auth = {};
      $ionicPlatform.ready(function(){
          var ref = new Firebase(FIREBASE_URL);
          auth = $firebaseAuth(ref);
      });
      return auth;
  }
]);

I am injecting the Auth factory in my ui-router resolve as dependency but by the time ui-router is configured the auth is empty because the platform ready kicks in afterwards.

app.config(function ($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('menu', {
      url: '/menu',
      abstract:true,
      cache: false,
      controller: 'MenuCtrl',
      templateUrl: 'templates/menu.html',
      resolve: {
        auth: function($state, Auth){
          //<--Auth is empty here when the app starts ----->
          return Auth.$requireAuth().catch(function(){
              $state.go('login'); //if not authenticated send back to login
          });
        }
      }
    })

How do i make sure the Auth factory is not empty before it is injected into ui-router?

I tried this -

app.factory("Auth", ["$firebaseAuth", "FIREBASE_URL","$ionicPlatform",
      function($firebaseAuth, FIREBASE_URL, $ionicPlatform) {
          return $ionicPlatform.ready(function(){
              var ref = new Firebase(FIREBASE_URL);
              return $firebaseAuth(ref);
          });
      }
    ]);

But this returns a promise which makes it even more complicated to use.

EDIT : I used promises in factory to fix the problem

app.factory("Auth", ["$firebaseAuth", "FIREBASE_URL","$ionicPlatform","$q",
  function($firebaseAuth, FIREBASE_URL, $ionicPlatform, $q) {
    var auth = {};
    return {
      getAuth : function(){
        var d = $q.defer();
        $ionicPlatform.ready().then(function(){
          var ref = new Firebase(FIREBASE_URL);
          auth = $firebaseAuth(ref);
          d.resolve(auth);
        });
        return d.promise;
      }
    };
  }
]);

This works but I am looking for a better solution.

Nikhil
  • 1,118
  • 1
  • 16
  • 33

2 Answers2

5

You can't return {} and then later expect that to magically become the $firebaseAuth instance, which is later set in an asynchronous function. See this question for some async education.

// "points" the x variable to object A
var x = {};
// "points" the x variable to an instance of $firebaseAuth()
// but not before {} is returned from the function
x = $firebaseAuth(...)

If you want to wait until the platform is ready before your resolve function is run, then simply chain the promises; that's pretty much the point of promises.

app.factory('IonicReady', function($q, $ionicFramework) {
   return $q(function(resolve, reject) {
      $ionicPlatform.ready(resolve);
   });
});

And then in your resolve:

resolve: {
   auth: function($state, Auth, IonicReady){
     return IonicReady
        .then(function() {
            return Auth.$requireAuth();
        })
        .catch(function(e) {
            console.error(e);
            $state.go('login');
        });
  }
}

Not being an Ionic expert, this simply begs the question: Why bootstrap Angular at all before Ionic is ready? Why is Angular not bootstrapped automatically when Ionic is loaded? It seems more appropriate to not perform any routing until the platform is ready to support this and to simplify the process from the gate. I don't know how to execute that part, but it seems like it solves the X rather than the Y.

Community
  • 1
  • 1
Kato
  • 38,684
  • 6
  • 110
  • 135
  • Thanks @Kato - I was in the same thought process of bootstrapping angular after Ionic is ready - It caused memory leaks and I did not know how to fix that. I am using promises in factory to fix the problem but I am not sure if that is how factories should be used..See edit. – Nikhil Jan 12 '16 at 23:36
0

Be sure to check out the official Firebase documentation and examples on https://www.firebase.com/docs/web/libraries/angular/guide/user-auth.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();
  }]
}

Your Auth factory can look like this then

app.factory("Auth", ["$firebaseAuth",
  function($firebaseAuth) {
    var ref = new Firebase("https://docs-sandbox.firebaseio.com");
    return $firebaseAuth(ref);
  }
]);

without a dependency on the ionic platform itself.

gmork
  • 21
  • 3
  • This does not work because you are creating new Firebase without wrapping it with platform ready.Also, some of my other factories also have the same issue..for example my message factory is also injected in resolve. ```app.factory('Messages', function($firebaseArray, FIREBASE_URL, $ionicPlatform){ var betweenUsers = ionic.Platform.ready(function(){ var refUserMessages = new Firebase(FIREBASE_URL+"/userMessages"); betweenUsers = function(path){ return $firebaseArray(refUserMessages.child(path)); } }); return betweenUsers; });``` – Nikhil Jan 12 '16 at 19:57
  • also, i have to wrap all firebase related calls with ionic platform ready. so $waitforauth does not solve the purpose – Nikhil Jan 12 '16 at 20:01
  • Why do you need to wrap all these calls in the ready function? Check for example https://www.firebase.com/docs/web/libraries/ionic/guide.html. – gmork Jan 12 '16 at 20:07
  • This is a known issue - https://forum.ionicframework.com/t/ionic-firebase-stuck-loading-forever/31146/18 I contacted the ionic backend team and they recommended I wrap all firebase inits in platform ready. It fixed the issue with firebase but i have problems with injecting dependencies before the platform is ready. – Nikhil Jan 12 '16 at 20:16
  • What about returning a new promise then, and resolving it with the firebase object in the ready function? – gmork Jan 12 '16 at 21:24