37

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?

2
  • 1
    Why not just a conditional ng-include? show one include if logged in, another if not Commented Oct 17, 2013 at 14:40
  • "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? Commented Oct 17, 2013 at 14:43

9 Answers 9

52

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.

Sign up to request clarification or add additional context in comments.

8 Comments

I don't seem to be able to use transitionTo in the onEnter function though?
You might have to check the value of $state.current to verify you're not doing anything circular, since onEnter is going to always trigger for your root state, even when entering child states.
I seem to be unable to transitionTo any state from within the onEnter function, including states which are not children of my home state. I am able to do it in the controller though, but unfortunately this waits until everything is loaded and shown in screen to run.
Because the transition isn't considered finalized until all resolves and all onEnter/onExit hooks have executed without error, at which point the $state globals are updated.
What about an exemple ? This answer is useless, everyone can google and read a doc.
|
8

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

Comments

5

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.

Comments

3

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.

1 Comment

FYI, use Transition Hooks instead of $stateChangeStart or $stateChangeSuccess. Source: ui-router.github.io/guide/ng1/…
2

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);
        }]
      });

Comments

1

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" );
}

3 Comments

Take facebook for example - if a guest accesses the facebook home page (facebook.com) they are shown a splash page through which they can sign in or sign up, if a user who has previously logged in accesses that same page, they are shown their profile. I'm looking to get the same URL for users both logged in and guest through use of states, as the profile page will have nested views (which will be accessed by url changes). I think the question title doesn't describe what I want very well!
@RyanConnolly As I understand the Angular $routePovider the whole purpose is to display different elements/pages based on URL variables. I assume [but don't know] that Facebook is doing that on the server side. If user is logged in; show wall else show login screen
I guess I need help thinking of a smart way to get around that limitation then! I can think of rough ways to do it but they all seem very hacky and I'm unsure if, in practise, they will work.
0

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.

3 Comments

I'm looking to achieve it without changing the url - is this possible?
So you want to show the login page without changing the url? So like in a modal? If you want to change the entire ng-view partial, then you're probably going to have to change the url so that the $routeProvider is updated and the $routeChange* events are fired...
FYI, use Transition Hooks instead of $stateChangeStart or $stateChangeSuccess. Source: ui-router.github.io/guide/ng1/…
0

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.

Comments

-1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.