Ryan Ryan - 2 months ago 9
AngularJS Question

Angular js / ui-router / restrict user from manually navigating to url (view)

I have spent the entire day on this (hobby-programmer, not a real one). I admit up front that the issue is my lack of understanding of the basic fundamentals of angular (and most programming for that matter). I am especially new to web development and need some help.

Anyways, I have a template that I'm using for learning purposes, that's all this is really. It's the 'ani-theme' from startangular.com. I built some basic logic to authenticate a user (type 'aaa' in the email lol, remember, its just for learning). This code works fine, and if 'aaa' is entered then the router will be triggered to move you to the dashboard.

The problem is that the user could just put the URL for the dashboard in the browser and go there.

I have a variable called "authed" that is set to true once they log in, but I cant seem to 'conditionally route' the user to the dashboard when they type the URL in manually. I have tried so many things but no luck.

After 5 hours of research, i think it is due to the asynchronous nature of angular OR scope issues. Or both. Probably neither. idk.

I saw so many other posts about $stateChangeStart but it went way over my head. Can someone point me in the right direction here or try to explain what is going on here in dummy terms. I mean it, I really don't know much so dumb it down, I wont be insulted.

APP.JS

var authed = false;
var done = false;
var yapp = angular
.module('yapp', [
'ui.router',
'ngAnimate',
'myAuth'
])
.config(function($stateProvider, $urlRouterProvider) {


$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');

$stateProvider
.state('base', {
abstract: true,
url: '',
templateUrl: 'views/base.html'
})
.state('login', {
url: '/login',
parent: 'base',
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
})
.state('dashboard', {
url: '/dashboard',
parent: 'base',
templateUrl: 'views/dashboard.html',
controller: 'DashboardCtrl'
})
.state('overview', {
url: '/overview',
parent: 'dashboard',
templateUrl: 'views/dashboard/overview.html'
})
.state('reports', {
url: '/reports',
parent: 'dashboard',
templateUrl: 'views/dashboard/reports.html'
});

});


LOGIN.JS

angular.module('yapp')
.controller('LoginCtrl', function($scope, $location, authFactory) {
$scope.submit = function(emailp) {
if(authFactory.checkAuth(emailp)) {
$location.path('/dashboard');
}else{
alert("WRONG");
}
}
});


AUTH.JS

var myAuth = angular.module('myAuth', [])
.factory('authFactory', function(){
var factory = {};
factory.checkAuth = function(emailp){
if(emailp == 'aaa') authed = true;
return(authed);
};
return factory;
});


IMPORTANT SIDE NOTE

I love advice and help, so please, if you see other things I'm doing that just look ridiculous, please call me out. It will help me a lot.

------------------------------------------------



EDIT EDIT EDIT EDIT EDIT

Thanks for the answers so far! I am going to try implementing @swestner 's answer and once it is working, I will study the 'why' part so I can really understand.

I do have another question on this same issue to clarify so that I can better understand why my other method wasn't working. I am very curious because it is a strange behavior.

So, You see my authed variable is declared in app.js, and then in the auth.js factory it is set to true or false depending on the users 'emailp'.
I added some logic to the app.js saying 'if authed is true, use these route commands, otherwise use these ones.

example:

console.log(authed) //this actually does print the correct value...
if(authed) { //however this doesnt work!
$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');
}else{
$urlRouterProvider.when('/dashboard', '/login');
$urlRouterProvider.otherwise('/login');
}


I can print the correct value, however the condition is always true.

HOWEVER, if I declare the variable authed2 RIGHT before the conditional statement it works fine!

var authed2 = true;

console.log(authed + " #1");
console.log(authed2 + ' #2');

if(authed2) {
$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');
}else{
$urlRouterProvider.when('/dashboard', '/login');
$urlRouterProvider.otherwise('/login');
}


The program knows both values to be true, I can even print them both right before the conditional, however when I use authed (set and declared elsewhere) the conditional doesnt work (even tho it seems to know the answer).

Its confusing me and I have to be missing some background behavior here.

Answer

The $stateChangeStart event is the proper place to handle this. This event will fire when you try to navigate to a url. At that point you can check if the user is authenticated, and if not, bounce them back to login.

You would hook up the event like this :

angular
  .module('yapp')
  .run(function ($rootScope, $state, authFactory) {
    $rootScope.$on('$stateChangeStart', function () {
      if(!authFactory.isAuthed()){
        $state.go('login')
      }
    })
  });

And update your auth factory to have the isAuthed method.

var myAuth = angular.module('myAuth', [])
  .factory('authFactory', function () {
    var factory = {};
    factory.checkAuth = function (emailp) {
      if (emailp == 'aaa') authed = true;
      return (authed);
    };

    factory.isAuthed = function () {
      return authed;
    }
    return factory;
  });
Comments