Eric Saupe
Eric Saupe's Blog

Eric Saupe's Blog

AngularJS $location Not Changing the Path

Eric Saupe's photo
Eric Saupe

Published on Feb 22, 2014

4 min read

In my adventures learning AngularJS I am confronted with strange problems all the time which is a really great thing. I love learning how to do new and intersting things in different programming environments. Today I was having problems with my URL not forwarding correctly on a callback method. Once a user was logged in I wanted the URL to forward to another page. I was using $location.path and setting it to a URL that was defined in my $routeProvider but it wasn't actually going to that page. A quick Google search led me to this Stack Overflow page, AngularJS $location not changing the path, with the exact problem I was having. The answer, it turns out, is to add $scope.apply() after any changes you've made. This does a refresh on the scope and applies any changes you've made that may not have been updated. The answer describes a conflict with another library in his case jQuery. I am using Firebase to log the user in and I believe the conflict is there. Here is my code and a little bit of explanation with it.

controllers.js

.controller('AuthController', ['$scope', '$rootScope', '$location', 'AuthService', function($scope, $rootScope, $location, AuthService) {
    $scope.login = function(email, password) {
      var ref = new Firebase('myfirebaseapp.firebaseio.com');
      var auth = new FirebaseSimpleLogin(ref, function(error, user) {
        if (error) {
          // an error occurred while attempting login
          console.log(error);
        } else if (user) {
          // user authenticated with Firebase
          $rootScope.user = user;
          $location.path('/stores').replace();
          $scope.$apply();
        } else {
          // user is logged out
        }
      });
      AuthService.login(email, password, auth);
    }
    $scope.createUser = function(email, password) {
      var ref = new Firebase('myfirebaseapp.firebaseio.com');
      var auth = new FirebaseSimpleLogin(ref, function(error, user) {
        if (error) {
          // an error occurred while attempting login
          console.log(error);
        } else if (user) {
          // user authenticated with Firebase
          $rootScope.user = user;
          $location.path('/stores').replace();
          $scope.$apply();
        } else {
          // user is logged out
        }
      });
      AuthService.createUser(email, password, auth);
    }
    $scope.logout = function() {
      var ref = new Firebase('myfirebaseapp.firebaseio.com');
      var auth = new FirebaseSimpleLogin(ref, function(error, user) {
        if (error) {
          // an error occurred while attempting login
          console.log(error);
        } else if (user) {
          // user authenticated with Firebase
        } else {
          // user is logged out
          $rootScope.user = null;
        }
      });
      AuthService.logout(auth);
    }
  }])

services.js

.factory('AuthService', ['$rootScope', '$location', function($rootScope, $location) {
    return {
        createUser: function(email, password, auth) {
            auth.logout();
          auth.createUser(email, password, function(error, user) {
            if (!error) {
              $rootScope.user = user;
            }
          });
        },
        login: function(email, password, auth) {
            auth.logout();
          auth.login('password', {
            email: email,
            password: password,
            rememberMe: false
          });
        },
        logout: function(auth) {
            auth.logout();
        }
      }
  }]);
The controllers.js file contains a simple controller to put my login, logout, and createUser functions in. I have off loaded the actual Firebase login to a service that I load in. I had a really hard time getting it all to work and found that putting the Firebase connections into the controller method and passing that to the service was the best route. I had the Firebase connection in the service but the callbacks were not firing quite in sync with the controller. This way I was able to get a more consistent timing of events. The FirebaseSimpleLogin has a callback that gets fired whenever an event happens with it. In our case that is user is created, user logs in, or user logs out. I then save the user data to the $rootScope to access it throughout my application and change the URL to a path of my choosing. At this point without the $scope.$apply() the URL would never actually get fired nor would the $scope realize that there was now a user object, though with the Chrome AngularJS plugin I could clearly see the data was in the scope. Using the $scope.$apply() cleared all of this up and it works great now. If anyone has any comments on this or any bit of my code above I am very open to hearing about it. I am still learning AngularJS and am still unsure of proper coding standards.
 
Share this