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.
.controller('AuthController', ['$scope', '$rootScope', '$location', 'AuthService', function($scope, $rootScope, $location, AuthService) {
$scope.login = function(email, password) {
var ref = new Firebase('https://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('https://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('https://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);
}
}])
.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.