14

I want to check an http services to see if a user is authenticated and if it has permission to see the certain page (controller) in AngularJS 1.2.0rc1.

I have this scenario: User A wants to visit http://www.example.com/content. Now this content should not be accessible when he or she is not authenticated. If the user is not authenticated, they should be redirected to http://www.example.com/login.

Now I managed to do this, but I see the rendered html of the content page briefly. I don't want this. How do I redirect to the login page, without rendering the content page?

I register my routes as such:

$routeProvider.when('/login', route.resolve('Login', false))
$routeProvider.when('/content', route.resolve('Content', true))

Now I've tried this:

$rootScope.$on('$routeChangeStart', function (event, route) {
    if (route.requiresLogin) {
        $http.get('/api/user/loggedin/').then(function (response) {
            if (response !== 'true') {
                $location.path('/login');
            }
        });
    }
});

Which works, but I still see the content of the html of the other page (content) flashing.

I've read that here that you can also try to change the resolve functionality when the route is resolved:

    var resolve = function (baseName, requiresLogin) {
    var routeDef = {};
    var dependencies = [routeConfig.getControllersDirectory() + baseName + 'Controller.js'];

    routeDef.templateUrl = routeConfig.getViewsDirectory() + baseName.toLowerCase() + '.html';
    routeDef.controller = baseName + 'Controller';
    routeDef.requiresLogin = requiresLogin;
    routeDef.resolve = {
        load: ['$q', '$rootScope', '$http', '$location', function ($q, $rootScope, $http, $location) {
            console.log(requiresLogin);
            if (requiresLogin) {
                return checkLoggedIn($q, $rootScope, $http, $location);
            } else {
                return resolveDependencies($q, $rootScope, dependencies);
            }
        }]
    };

    return routeDef;
}

But here the page is also displayed briefly.

Hope that you can help me out.

Community
  • 1
  • 1
Aldentev
  • 490
  • 2
  • 6
  • 18

1 Answers1

22

The first answer should work, but to do it using $q I would use defer.resolve() or defer.reject("rejection_reason") then I would listen for a $routeChangeError in a top-level controller that is bound to an an element outside of the ng-view that the routeProvider manages.

So in your HTML, something like:

<body ng-app="app" ng-controller="DocumentCtrl">
  <div ng-view>
  </div>
</body>

Then that route of yours would be defined something like:

when('/home', {
  templateUrl: templateUrl blah,
  controller: baseName + 'Controller',
  resolve: {
    load: function($q, MY_USER_SERVICE){
      var defer = $q.defer();
      if(~MY USER LOGGED_IN LOGIC RETURNS TRUE~){
        defer.resolve();
      } else {
        defer.reject("not_logged_in");
      }
      return defer.promise;

    }
  }
}).

Then my DocumentCtrl controller would contain:

$scope.$on("$routeChangeError", function(evt,current,previous,rejection){
  if(rejection == "not_logged_in"){
    //DO SOMETHING
  } else {
    //OR DO SOMETHING ELSE
  }
});

I'm running the above and there's no flash of any views that shouldn't be loaded.

Ade
  • 2,961
  • 4
  • 30
  • 47
  • Confirmed as the way to deal with this, while not showing the original routed page. Promises are not necessary, a simple throw new Error ('') is sufficient. Key is that only an error will stop the current navigation. Maybey a future version of Angular will bring us a decent way to handle conditional (injected) redirection. – Remco Oct 10 '14 at 15:47
  • Oh, interesting. Se we can simply return true in the load function, or conditionally throw an error above that if we don't want to allow access? In every example I have seen a promises have been used. – Ade Oct 10 '14 at 17:36
  • Using UI-router has made all these easy. Simply writing `$state.go('state')` inside `resolve` redirects you before loading the controller. – Meliodas Apr 22 '17 at 10:24