2

I would like to have two home pages, the first would be for users who have not logged in and the second for users that are logged in.

This is my current set up:

.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
    $urlRouterProvider
      .otherwise('/');

    $locationProvider.html5Mode(true);
    $httpProvider.interceptors.push('authInterceptor');
  })

  .factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
    return {
      // Add authorization token to headers
      request: function (config) {
        config.headers = config.headers || {};
        if ($cookieStore.get('token')) {
          config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
        }
        return config;
      },

      // Intercept 401s and redirect you to login
      responseError: function(response) {
        if(response.status === 401) {
          $location.path('/login');
          // remove any stale tokens
          $cookieStore.remove('token');
          return $q.reject(response);
        }
        else {
          return $q.reject(response);
        }
      }
    };
  })

  .run(function ($rootScope, $location, Auth) {
    // Redirect to login if route requires auth and you're not logged in
    $rootScope.$on('$stateChangeStart', function (event, next) {
      if (next.authenticate && !Auth.isLoggedIn()) {
        $location.path('/login');
      }
    });
  });
.config(function ($stateProvider) {
        $stateProvider
          .state('main', {
            url: '/',
            templateUrl: 'app/main/main.html',
            controller: 'MainCtrl',
            title: 'Home',
            mainClass: 'home',
            headerSearch: true
          });
      });

How could I reconfigure this so I could do something like the following:

.config(function ($stateProvider) {
    $stateProvider
      .state('welcome', {
        url: '/',
        templateUrl: 'app/welcome/welcome.html',
        controller: 'WelcomeCtrl',
        title: 'Welcome',
        mainClass: 'welcome',
        isLoggedIn: false
      });
     $stateProvider
      .state('main', {
        url: '/',
        templateUrl: 'app/main/main.html',
        controller: 'MainCtrl',
        title: 'Home',
        mainClass: 'home',
        isLoggedIn: true
      });
  });
Daimz
  • 3,243
  • 14
  • 49
  • 76

4 Answers4

5

Just wanted to show, how we can manage authentication driven access to states. Based on this answer and its plunker, we can enrich each state (which should be accessible only for authenticated users) with a data setting, explained here: Attach Custom Data to State Objects (cite:)

You can attach custom data to the state object (we recommend using a data property to avoid conflicts)...

So let's have some states with public access:

// SEE no explicit data defined
.state('public',{
    url : '/public',
    template : '<div>public</div>',
})
// the log-on screen
.state('login',{
    url : '/login',
    templateUrl : 'tpl.login.html',
    controller : 'UserCtrl',
})
... 

And some with private access:

// DATA is used - saying I need authentication
.state('some',{
    url : '/some',
    template : '<div>some</div>',
    data : {requiresLogin : true }, // HERE
})
.state('other',{
    url : '/other',
    template : '<div>other</div>',
    data : {requiresLogin : true }, // HERE
})

And this could be hooked on on the state change:

.run(['$rootScope', '$state', 'User', function($rootScope, $state, User)
{

  $rootScope.$on('$stateChangeStart'
    , function(event, toState, toParams, fromState, fromParams) {

    var isAuthenticationRequired =  toState.data 
          && toState.data.requiresLogin 
          && !User.isLoggedIn
      ;

    if(isAuthenticationRequired)
    {
      event.preventDefault();
      $state.go('login');
    }
  });
}])

See all that in action here

There is similar Q & A were I try to show the concept of redirecting Authenticated and Not Authenticated user:

maybe that could help to get some idea, how we can use ui-router, and its event '$stateChangeStart' to hook on our decision manager - and its forced redirecting...

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
2

the code should be something like this

     $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState)                { //, fromParams
       console.log(toState.name + "," + fromState.name);
        if(fromState.name === "") {
          if (Auth.isLoggedIn()) {
              $state.go('welcome');
               event.preventDefault();
          } else {
            $state.go('home');
               event.preventDefault();
        } else {
           if (toState.authenticate && !Auth.isLoggedIn()) {
               $toState.go('login');
               event.preventDefault();
           }
        }
    }

so if user entering the application, then if he is logged in take him to welcome else take him to home.

once he is inside, then if he hits some route which needs auth.. then redirect him to login page..

sorry if i did not understood you requirement fully...

harishr
  • 17,807
  • 9
  • 78
  • 125
  • I gave this a try but all I got was an error saying `stateChangeStart` doesn't exist. I removed it so just using `$rootScope.$on('$stateChangeStart');` and that didn't throw an error but instead it just didn't work. – Daimz Jul 29 '14 at 13:23
  • Am I meant to put this inside `.run(function ($rootScope, $location, Auth) {`? – Daimz Jul 29 '14 at 14:00
  • I found the missing colon but now I get `ReferenceError: $state is not defined` – Daimz Jul 29 '14 at 14:01
  • Ok, the now gives `TypeError: undefined is not a function` – Daimz Jul 29 '14 at 14:04
  • 1
    have you inject your auth service?? can you setup plunker, ?? – harishr Jul 29 '14 at 14:06
  • This will show how I have set up the JS if you look in scripts.js http://plnkr.co/edit/9IrNSPLEtExn6pddrTsg – Daimz Jul 29 '14 at 14:11
  • can you please provide complete plunker.. not just js.. you have two modules with same name btw.. can you please read basics of ui router – harishr Jul 29 '14 at 14:21
  • I don't know how to set up the auth in Plunkr as I am using Angular-Fullstack. And the module names don't seem to be interfering with anything else. – Daimz Jul 29 '14 at 14:25
  • but the most basic part is, you can have two modules with same name... atleast as far as how i understand it... – harishr Jul 29 '14 at 14:26
  • hummm, I will look into that, but for now things are working (you'll have to bear with me I am trying to learn angular atm so I might be a bit slow and thats the reason why I'm trying to understand). I have readdressed the plunkr, so perhaps it's a bit more readable, but it still doesn't work as the whole auth part is a bit beyond me when it comes to setting up a plunkr. But atleast you can see where I am making the mentioned changes and perhaps where I am going wrong. – Daimz Jul 29 '14 at 14:40
  • I now get this error: `RangeError: Maximum call stack size exceeded` – Daimz Jul 29 '14 at 14:58
  • you are much closer to solution.. check the edit.. added `event.preventDefault();` to all the routes – harishr Jul 29 '14 at 15:00
  • It works with `event.preventDefault();` on all urls except `The maximum call stack size exceeded` error remains. but If I change `if(fromState.name === "")` to `if(fromState.name = "")` the error goes but then it doesn't update the states. – Daimz Jul 29 '14 at 15:21
  • try to debug why is it comingn so many times in this loop and you would mostly get the solution – harishr Jul 29 '14 at 15:40
  • I have tried to debug but all I can see is that this `(anonymous function) angular.js:7299 Scope.$broadcast angular.js:12889 transitionTo angular-ui-router.js:2032 go` is being called over and over again. I even read this https://github.com/angular-ui/ui-router/issues/618 which covers a similar issue but no luck. This method simply causes my app to either crash or create an infinite loop on the state. I am simply wanting to redirect to a different state if a user logs in, surely it's not to difficult. – Daimz Jul 30 '14 at 03:16
  • The other problem I just relalised is if I go from the initial state to another, lets say '/login' and then go back to '/' (still not logged in) I will see the 'home' state which is meant to be hidden to visitors and only visible to people who are logged in. I'm guessing this is due to using `fromState` is there another way around this? – Daimz Jul 30 '14 at 04:25
  • `console.log(toState.name + "," + fromState.name);` added console.log.. can u check the output in console – harishr Jul 30 '14 at 04:32
  • All I get is `400 console messages are not shown. 897 welcome, 3 welcome,` But as I say this only works when I first load the app when that state is '' but if I go to about and then come back it now sees state from not as '' but 'about' and loads 'home' but it should only ever load home if a user is logged in regardless of what pages they visit prior to logging in. – Daimz Jul 30 '14 at 04:49
  • please analyze tostate and fromstate and debug little bit should solve the problem... – harishr Jul 30 '14 at 04:56
  • What you have done is exactly what I have done. But it still doesn't change the fact that my question originally was how do I make it so that if a user comes to my app and is not logged in they see 'welcome' and if they come and they are logged in they see 'home' this means that no matter what page they visit if they come back to the base url '/' they will see 'welcome' only logged in users will ever see 'home'. – Daimz Jul 30 '14 at 05:14
  • Where should `rootscope` code go? Inside a `.config(..)`? – CodyBugstein Jan 12 '15 at 19:13
1
.config(function ($stateProvider,$rootScope) {
$stateProvider
  .state('welcome', {
    url: '/',
    templateUrl: 'app/welcome/welcome.html',
    controller: 'WelcomeCtrl',
    onEnter: function() {
      if (userIsLoggedIn()) {
         $stateProvider.go('home');
      }
  });

});

Code Whisperer
  • 22,959
  • 20
  • 67
  • 85
  • I can't seem to get this working. It still just defaults to 'home' – Daimz Jul 29 '14 at 13:22
  • 1
    I found that a brace was missing so I get the following error: `Uncaught Error: [$injector:modulerr] Failed to instantiate module cbuiRouterApp due to: Error: [$injector:unpr] Unknown provider: $rootScope` – Daimz Jul 29 '14 at 13:31
0

I had problem like this and I solved it like this

 .run(function ($rootScope, $location, AuthService) {

        // start showing PRELOADER because we doing async call and we dont know when it will be resolved/rej
        AuthService.checkLoginStatus().then(
            (resp) => {
                // fire logged in user event
                $rootScope.$broadcast('userLogged',resp);
                $location.path(YourHomePageUrl);

            },
            (err)=> {  
                // Check that the user is not authorized redirect to login page                  
                    $location.path(loginURL);
                }
            }).finally(
            ()=> {
                // finnaly Set a watch on the $routeChangeStart
                /// Hide PRELOADER
                $rootScope.$on('$routeChangeStart',
                    function (evt, next, curr) {
                      if (!AuthService.isLoggedIn()) {
                                $location.path(art7.API.loginURL);
                            }

                    });
            }
        )


    });

and im not using interceptor for handling 401 not authorized errors, thats my solution

Narek Mamikonyan
  • 4,601
  • 2
  • 24
  • 30