38
votes

I can't figure out a reasonable way, which doesn't feel like a hack, to solve this rather trivial problem.

I want a guest to see a splash page when they access the index of the website and a logged in user to see their profile, with each page having it's own template and controller. Ideally, there would be two states for one url, and somehow I would be able to automatically alter the active one depending on the loggin status. Both of these views will have their own nested views so ng-include cannot be used (I assume).

I'm quite new to angular and ui router and think I might be overlooking an easy solution to the problem.

Could it be done with named views and ng-show?

9
Why not just a conditional ng-include? show one include if logged in, another if not - Roger Johansson
"Both of these views will have their own nested views so ng-include cannot be used (I assume)." ..can't find anything like ng-include built into ui-router, or is nested views possible with it/ some kind of work around? - Ryan Connolly

9 Answers

53
votes

If you're using UI Router, just create three states: the root state, with the '/' URL, and two direct descendant states with no URLs. In the onEnter of the root state, you detect the state of the user and transition to the correct child state accordingly. This gives the appearance of keeping the same URL for both child states, but allows you to have to separate states with separate configurations.

8
votes

The templateUrl can be a function as well so you can check the logged in status and return a different view and define the controller in the view rather than as part of the state configuration

5
votes

My Solution:

angular.module('myApp')
.config(function ($stateProvider) {
    $stateProvider
        .state('main', {
            url: '/',
            controller: function (Auth, $state) {
                if (someCondition) {
                    $state.go('state1');
                } else {
                    $state.go('state2');
                }
            }
        });
});

where state 1 and state 2 are defined elsewhere.

4
votes

For docs purposes, I used:

$rootScope.$on('$stateChangeStart', function(event, toState) {
  if ((toState.name !== 'login') && (!$localStorage.nickname)) {
    event.preventDefault();
    $state.go('login');
  }
});

Using $routeChangeStart didn't work for me.

2
votes

It is used for me conditional view in ui-route

$stateProvider.state('dashboard.home', {
        url: '/dashboard',
        controller: 'MainCtrl',
       // templateUrl: $rootScope.active_admin_template,
        templateProvider: ['$stateParams', '$templateRequest','$rootScope', function ($stateParams, templateRequest,$rootScope) {
          var templateUrl ='';
          if ($rootScope.current_user.role == 'MANAGER'){
            templateUrl ='views/manager_portal/dashboard.html';
          }else{
            templateUrl ='views/dashboard/home.html';
          }
          return templateRequest(templateUrl);
        }]
      });
1
votes

If I understand the question; you want to make sure that the user who hasn't logged in cannot see a page that requires log in. Is that correct?

I've done so with code like this inside a controller:

if(!'some condition that determines if user has access to a page'){
 $location.path( "/login" );
}
1
votes

Anywhere (probably in some high-level controller) you should be able to just bind a '$routeChangeStart' event to the $rootScope and do your check then:

$rootScope.$on('$routeChangeStart', function(next, current){
    if(next != '/login' && !userLoggedIn){
        $location.path( "/login" );
    }
});

This will get fired every time a new route is set, even on the first visit to the page.

0
votes

The way I've done this is pretty simple. I made one for our A/B testing strategy. This is the gist:

resolve: {
   swapTemplate: function(service) {
      // all of this logic is in a service
      if (inAbTest) {
         this.self.templateUrl = '/new/template.html';
      }
   }
   ... other resolves
}

This gets called before the template is downloaded and therefor you're allowed to swap out the template url.

-1
votes

In my case, if two states can share logic of same controller, conditional template is a good choice. Otherwise, creating separate states is a good option.