Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly implement angularFireAuth?

I have changed the official AngularJS documentation to use Firebase (the phone tutorial).

This is the router of my app:

angular.module('phonecat', ['firebase']).
 config(['$routeProvider', function($routeProvider) {
 $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl,
        authRequired: false,
        pathTo: '/phones'
      }).
      when('/phones/:age', {
        templateUrl: 'partials/phone-detail.html',
        controller: PhoneDetailCtrl,
        authRequired: true,
        pathTo: '/phones/:phoneId'
      }).
      when('/login', {
        templateUrl: 'partials/login.html',
        controller: LoginCtrl,
        authRequired: false,
        pathTo: '/login'
      }).
      otherwise({
        redirectTo: '/phones'
      }); }]);

This is how the controller looks like - but it is probably incorrect. The login function gets called, however I'm not sure what to do next. How should I redirect the user from the login page to the phone-detail.html page?

'use strict';

function PhoneListCtrl($scope, angularFire, angularFireAuth) {
  var url = 'https://<link>.firebaseio.com/';
  var promise = angularFire(url, $scope, 'phones', []);
  angularFireAuth.initialize(url, {scope: $scope, name: "user"});
}

function PhoneDetailCtrl($scope, $routeParams, angularFire, angularFireAuth) {
  var url = 'https://<link>.firebaseio.com/' + $routeParams.age;
  angularFireAuth.initialize(url, {scope: $scope, path: "/login"});
  $scope.$on("angularFireAuth:login", function(evt, user) {
    var promise = angularFire(url, $scope, 'phone', {}); 
  });
  $scope.$on("angularFireAuth:logout", function(evt) {
    console.log("you are logged out.");
  });
  $scope.$on("angularFireAuth:error", function(evt, err) {
    console.log(err);
  });
}

function LoginCtrl($scope, angularFire, angularFireAuth) {
  var url = 'https://<link>.firebaseio.com';
  $scope.form = {};
  $scope.login = function() {
    console.log("called");
    var username = $scope.form.username;
    var password = $scope.form.password;
    angularFireAuth.login('password', {
        email: username,
        password: password,
        rememberMe: false
      });
  };
}

The login.html looks like this:

<input type="text" name="username" ng-model="form.username"><br>
<input type="text" name="password" ng-model="form.password"><br>
<button name="login" id="login" ng-click="login()">login</button>

What I'd like to achieve is this:

  1. List all the phones
  2. If the user clicks to get the details of one of them, check if they are authenticated
  3. if yes, show the phone details -- if not, redirect them to the login page.
  4. After logging in, redirect them to the phone detail view.

I'm struggling with points 3 and 4.

Update

After the comments from Anant -- I found something very interesting. I added a few debug msgs to angularFire.js and for the time being I changed the authRequired from true to false in my controller above.

If navigate to /phones - I get a list returned back from firebase as expected. Inside _loggedIn() I added console.log(user) which returns a user object (also, inside initialise I added a debug statement: https://github.com/firebase/angularFire/blob/master/angularFire.js#L437 -- both confirm that I have a valid user, already logged in.

If I then click on an item, I get what I expect - the username and the actual page loads (see the html below). If I refresh that page (http:///phones/#/0) I get the right page again and in the console, I still see a valid user object, indicating that the user is still logged in

Here's the HTML for both phone-list.html and phone-details.html:

phone-list.html
<div class="container-fluid">
    <div class="row-fluid">
      <div class="span4">
        <!--Body content-->

        <ul class="phones">
          <li ng-repeat="phone in phones">
            <a href="#/phones/{{phone.age}}">{{phone.id}}</a>
            <p>{{phone.snippet}}</p>
          </li>
        </ul>
      </div>
    </div>
  </div>

phone-detail.html
<span ng-show="user">
  {{user.email}} | <a ng-click="logout()">Logout</a>
  you are look looking at {{ phone.name }}
</span>
<span ng-hide="user">
  login pls!
</span>

And a snippet from the JSON (that is now part of Firebase):

[
{
    "age": 0, 
    "id": "motorola-xoom-with-wi-fi", 
    "imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg", 
    "name": "Motorola XOOM\u2122 with Wi-Fi", 
    "snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
}, 
{
    "age": 1, 
    "id": "motorola-xoom", 
    "imageUrl": "img/phones/motorola-xoom.0.jpg", 
    "name": "MOTOROLA XOOM\u2122", 
    "snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
}...etc

If I then change the authRequired back to true - and if the user object is available, then I get an endless loop of page loads - first it's /login then it gets automatically redirected /phone/0 and immediately back to /login again, and this happens until the browser crashes.

Update 2

After adding some further debug lines and poking around with the code I've come up with this solution:

add the initialisation to the LoginCtrl:

var url = 'https://<link>.firebaseio.com/'; 
angularFireAuth.initialize(url, {scope: $scope});

In angularFire.js, I commented out lines 406 and 407:

//this._redirectTo = null;
//this._authenticated = false;

Appended the code around line 438, essentially I added this._authenticated = trueand this._redirectTo = $route.current.pathTo - as well as this._authenticated = false to the else statement.

var client = new FirebaseSimpleLogin(this._ref, function(err, user) {
  self._cb(err, user);
    if (err) {
      $rootScope.$broadcast("angularFireAuth:error", err);
    } else if (user) {
        this._authenticated = true;
        this._redirectTo = $route.current.pathTo;
        self._loggedIn(user)
    } else {
        this._authenticated = false;
        self._loggedOut();
    }
  });
  this._authClient = client;
},

The only scenario where this does not work is when a user is logged in and I navigate to http://<host>/#/login - the $route.current.pathTo will be equal to /login and at the moment I'm not 100% sure how to overcome this. Any thoughts on this Anant?

like image 351
Tamas Avatar asked Oct 04 '22 11:10

Tamas


1 Answers

I have opened Issue 72 on GitHub: https://github.com/firebase/angularFire/issues/72

The interim solution for the time being is to modify angularFire.js and:

Remove the following two lines from the initialise function:

this._redirectTo = null;

this._authenticated = false;

Modify angularFire.js around line 438 (the declaration of var client) to read the following:

var client = new FirebaseSimpleLogin(this._ref, function(err, user) {
  self._cb(err, user);
    if (err) {
      $rootScope.$broadcast("angularFireAuth:error", err);
    } else if (user) {
        this._authenticated = true;
        this._redirectTo = $route.current.pathTo;
        self._loggedIn(user)
    } else {
        this._authenticated = false;
        self._loggedOut();
    }
  });`
  this._authClient = client;
},

As mentioned above this fixes nearly everything - the only problem seems to be that if there's a valid user object (i.e. someone is logged in) navigating to /login should redirect the user to another page, at the moment nothing happens. The GitHub issue should have further information on this hopefully soon.

like image 65
Tamas Avatar answered Oct 12 '22 10:10

Tamas