So I am trying to figure out the easiest route for authentication with AngularJS and Google Plus. I have worked with angular-google-plus and it is great, spits back all my user information. I Then wanted to get back if a user is registered or not when they come to a new page or controller in my project. So I found this post which basically said the project needs to extend the api. Fine, but he implements a watcher that, when executed in my Controller works for the gapi, but when it checks for the gapi.auth it returns undefined. My console.log tells me both of them are there and are objects. Here is my controller code:
.controller('LoginController', ['$scope', '$state', '$window', '$http','$rootScope', 'GooglePlus', function ($scope, $state, $window, $http, $rootScope, GooglePlus) {
var checkGoogleReady = $rootScope.$watch(function() {
console.log(gapi);
$scope.login = function() {
GooglePlus.login().then(function (authResult) {
console.log(authResult);
GooglePlus.getUser().then(function (user) {
console.log(user);
});
}, function (err) {
console.log(err);
});
};
}])
So the stuff inside the login() function all works, returns everything I need. But outside it, it fails to get gapi.auth and never changes after that. I feel like it checks, finds gapi, then changes gapi.auth and then tries to wait for another change, never getting the gapi.auth. Here is a picture of the console:
So you can see all 3 objects actually have the auth property on them. So I am not sure why the gapi.auth is coming back undefined.
Related
I'm wondering how I can route to a different view when I'm working with this Pubnub function here.
QueueController
Pubnub.hereNow(
{
includeUUIDs: true,
includeState: true
},
function (status, response){
console.log(response);
//do some algorithm with response
$location.path('/chat');
}
);
So basically I'm getting the response from this hereNow function and then when the algorithm passes a condition inside, I want it to go from my QueueController to my ChatController so the users can begin chatting! I even removed the algorithm from the function and had the route just by itself, but it doesn't work at all.
When I put the $location.path('/chat') outside of my hereNow function, it does route very nicely.
So how does one route to a different view in this case?
Just discovered this answer and it seems to be working!
Pubnub.hereNow(
{
includeUUIDs: true,
includeState: true
},
function (status, response){
console.log(response);
$rootScope.$apply(function(){
$location.path('/chat');
});
}
);
I added the $rootScope as a dependency in my QueueController
app.controller('QueueController', ['$scope', '$location', '$rootScope', 'languageService', 'Pubnub',
function($scope, $location, $rootScope, languageService, Pubnub){ ... }]);
I'm trying to make a login session persist upon a page refresh but I'm not being successful in this matter. I took a look at this solution found here which uses the $firebaseSimpleLogin service to maintain a login session.
From that solution all I've done is basically deleted his wrapper that points to the Firebase instance, replaced it with my own var ref and put it as an argument of the $firebaseSimpleLogin. I also implemented array notation since I've done it with my controllers as well.
However, this function isn't even being executed because when I run the code it throws this error:
Uncaught Error: [$injector:unpr] http://errors.angularjs.org/1.3.10/$injector/unpr?p0=%24firebaseSimpleLoginProvider%20%3C-%20%24firebaseSimpleLogin
Does anyone know why I'm getting this? I can't see anything wrong with my injections like the error is telling me.
var pieShopApp = angular.module('pieShopApp', ['ngRoute', 'ngAnimate', 'firebase']);
var ref = new Firebase ('https://pie-shop.firebaseio.com/users');
pieShopApp.run(['$rootScope', '$location', '$firebaseSimpleLogin', function($rootScope, $location, $firebaseSimpleLogin) {
$rootScope.auth = $firebaseSimpleLogin(ref);
$rootScope.auth.$getCurrentUser().then(function(user) {
if (user) {
$rootScope.currentUser = user;
console.log($rootScope.currentUser);
}
else {
$location.path('/pies/login');
}
});
}]);
Let's say I have 4 routes - 2 require the user to be logged in, 2 do not. My app init looks like:
$routeProvider.when('/open1',{templateUrl:'/open1.html',controller:'Open1'});
$routeProvider.when('/open2',{templateUrl:'/open2.html',controller:'Open2'});
$routeProvider.when('/secure1',{templateUrl:'/secure1.html',controller:'Secure1'});
$routeProvider.when('/secure2',{templateUrl:'/secure2.html',controller:'Secure2'});
Routes /open1 and /open2 are open to all, while routes /secure1 and /secure2 require the user to be logged in and, if not, take some action, e.g. redirect to login or launch a warning. I can determine the user's state by using my Auth service and calling, e.g., Auth.isLogin(). So the result would be:
going to /open1 and /open2 always go to the template and controller declared above
if Auth.isLogin() returns true, /secure1 and /secure2 go to the above-declared template and controller
if Auth.isLogin() returns false, /secure1 and /secure2 take some other action, e.g. $location.path('/login')
I could put logic in the Secure1 and Secure2 controllers that checks, but that is repetitive and mixes up responsibilities, makes them harder to test, etc.
Is there a way that I can use the $routeProvider to declare, "check this route and this route and if not, redirect"? I was thinking of using resolve somehow, but not quite sure how to work it in (docs on resolve are not very clear, and few helpful examples).
EDIT:
based on the answers below, it appears there are three philosophies for doing this:
Using resolve to check logged in and fail the promise, and then catching the $routeChangeError event to redirect http://www.sitepoint.com/implementing-authentication-angular-applications/
Using just $routeChangeStart event to check logged in and redirect http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/
Using just resolve to check logged in and redirect http://midgetontoes.com/blog/2014/08/31/angularjs-check-user-login
The 2nd option is what the two answerers have suggested.
As in my comments above, there are 3 different paths (plus the ability to use a directive if you want to control it from within html templates). I ended up following
https://midgetontoes.com/angularjs-check-user-login/
which essentially is as follows:
$routeProvider.when('/secure', {
templateUrl: '/secure.html',
controller: 'Secure',
resolve:{
loggedIn:onlyLoggedIn
}
});
And then onlyLoggedIn:
var onlyLoggedIn = function ($location,$q,Auth) {
var deferred = $q.defer();
if (Auth.isLogin()) {
deferred.resolve();
} else {
deferred.reject();
$location.url('/login');
}
return deferred.promise;
};
Simple, works like a charm. If I ever need a directive, I will pull this piece into a service.
This blog post deals with user authentication in AngularJS using directives.
The $route service emits $routeChangeStart before a route change.
If you don't use directives, you can catch that event by calling app.run (you can place it after the code where you define the routes [app.config]). For example:
For full disclosure I use ui.router and this is an adapted code from $stateChangeStart I use in my app
var app = angular.module('app');
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/open1',{templateUrl:'/open1.html',controller:'Open1'});
$routeProvider.when('/open2',{templateUrl:'/open2.html',controller:'Open2'});
$routeProvider.when('/secure1',{templateUrl:'/secure1.html',controller:'Secure1'});
$routeProvider.when('/secure2',{templateUrl:'/secure2.html',controller:'Secure2'});
}]);
app.run(['$rootScope', '$location', 'Auth', function($rootScope, $location, Auth) {
$rootScope.$on('$routeChangeStart', function(event, currRoute, prevRoute){
var logged = Auth.isLogin();
//check if the user is going to the login page
// i use ui.route so not exactly sure about this one but you get the picture
var appTo = currRoute.path.indexOf('/secure') !== -1;
if(appTo && !logged) {
event.preventDefault();
$location.path('/login');
}
});
}]);
I had the same problem and I did it this way:
var app = angular.module('myModule',["ui-bootstrap"]);
And then listen for a locationchange in the app (this will also trigger onEnter of a page)
app.run(function ($rootScope, $location, $cookieStore) {
$rootScope.$on("$locationChangeStart", function (event, next, current) {
//Here you can check whatever you want (servercall, cookie...)
});
}
I Hope this helps!
I look for a user's session in the run process, and i would like to open a modal if i got a specific information from the User.
The thing is i need to send the scope to the modal, so i have to open it from a controller.
The problem is, when angular load controller, the user is not logged yet.
I tried to $location.path('/'); after the run async process, but it doesn't call the indexCtrl twice (because i am already on the view he is binding).
So my question is, how could i "refresh" my controller at the User.getSession() callback or how could i approach the thing differently ?
angular.module('TEST').run(function($rootScope, $window, AuthenticationManager, $location) {
AuthenticationManager.getSession(function() {
$location.path('/'); //Unfortunately useless because it doesn't call the indexCtrl twice
});
};
angular.module('TEST').controller('indexCrtl', function($scope, $User) {
if ($User.nb_points === 10) //Not fired because the user is not logged at the first load
var Modal = $modal({scope: $scope, show: false, template: 'modal.html'});
};
I think you are looking for $route.reload()
Causes $route service to reload the current route even if $location
hasn't changed.
As a result of that, ngView creates new scope, reinstantiates the
controller.
Example on jsfiddle
I'm using the boilerplate stack from MEAN.io and finding it quite a good starting point, however I'm having trouble mixing the different routing commands. My app is going to have a simple signin page which is public, everything else is hidden behind that. I can check if the user is authenticated no problem, but I cannot for the life of me get Angular to load the signin page from the server. I already have a signin button on my html page that calls the correct route no problem at all, I just can't do the same thing from code.
The $location.path('/signin'); code doesn't call the server because it leaves the hash in the path
My Angular controller
angular.module('tms.tweets').controller('TweetsController', ['$scope', '$routeParams',
'$location', '$resource', 'Global', 'Tweets', function ($scope, $routeParams, $location,
$resource, Global, Tweets) {
$scope.global = Global;
$scope.find = function() {
if(Global.authenticated){
console.log(Global.authenticated);
Tweets.query(function(tweets) {
console.log("Tweets at Angular Controller: " + tweets.length);
$scope.tweets = tweets;
});
}
else{
console.log("Unauthorized");
$location.path('/signin');
}
};
}]);
Found the answer to my redirect issue, I swapped $location.path for
$window.location.href = '/signin';
For my purposes calling $window.location.href = '/newpath'; from inside my controller was not working.
I've been using the following function if I need to reload within the controller:
$scope.changeRoute = function(url, forceReload) {
$scope = $scope || angular.element(document).scope();
if(forceReload || $scope.$$phase) { // that's right TWO dollar signs: $$phase
window.location = url;
} else {
$location.path(url);
$scope.$apply();
}
};
Then you would call it like this:
$scope.changeRoute('#/newpath');
I will say though that doing this should be avoided, and that adding a run phase to your app's configuration phase should be preferred. You can read more about adding a run phase to your app configuration here: http://www.angularjshub.com/examples/modules/configurationrunphases/