Best way to show first view dynamically when starting app

I would like to dynamically change the first view a user sees when the app starts.
for instance if a user is already logged in I don’t want to show the login screen but the main dashboard, but if he’s not logged in I want to show the login screen.
You can also start the app with an nfc tag and I would like to show a different first view if that happens as well.

I currently have one default view I can show,
currently:

$urlRouterProvider.otherwise('/tab/home');

What is the best way to dynamically show the first view?
I’ve found several solutions:

  1. the simpelest is to use $state.go() in the ionicplatform.ready() function.
    That works fine as long as I start my navigation at a defined state (for instance http://localhost:8100/#/login) but when I start at an undefined state and the ‘otherwise’ kicks in (for instance http://localhost:8100/), the state.go does not take effect.
    Somehow the otherwise navigation occurs after the ionicplatform.ready() function.

  2. As mentioned in this post:
    Showing home page instead of login if already logged in without animation
    You could listen to $stateChangeStart and but some logic in there. The problem with this solution is that it fires on every state change and I only need it on the first view.

  3. Create a ‘landing page’ view with for instance a spinning loader icon and start your navigation from there. But then you need to handle the fact that the page comes in your page history and people should not be able to navigate back to it.

  4. My current solution based on a post from Raymond Camden : Ionic and Cordova’s DeviceReady. Don’t do a state.go() but a location.path change. I had to modify it slightly to get rid of this error, Error digest already in progress. But that seems to work.
    So my current code is:

 if (AuthService.refreshToken) {
        $timeout(function() {
            $location.path('/tab/home');
            $rootScope.$apply();
        });
  }
  else {
        $timeout(function() {
            $location.path('/login');
            $rootScope.$apply();
        });
    }

And that seems to work for now.

but I can’t stop wondering if there isn’t a more elegant solution for this problem.
Did someone find another (better) solution for this requirement?

I’m also frustrated about this stuff. There is no bestpractice for this out there…
Now im doing it with the $stateChangeStart event.

I also thought about useing resolve in state but i guess it’s not a good idea.

Could you guy’s from the great ionic framework (@perry, @mhartington, @max, @adam, …) be so kind and give us a best practice workflow for that?
I read really every post about this topic here in forum and everywhere else and was not able to find a best practice solution. for this.

Thanks for your help guys!!!

My solution is to add resolve for the state in the $stateProvider. I think it’s quite elegant. Below is a simplified example.

First add this in the routing config for the the initial view (dashboard in this case):

$stateProvider.state('app.dashboard', {
    url: '/dashboard',
    views: {
        'menuContent': {
            templateUrl: 'app/dashboard/dashboard.html',
            controller: 'DashboardCtrl'
        }
    },
    resolve: {
        'currentAuth': ['Auth', function resolveControllerAuth(Auth) {
            // the controller will not load until the promise has resolved
            // the Auth service will resolve if the user is logged in and the view will be displayed
            // if user is not logged in the Auth service will redirect to login
            return Auth.requireLogin();
        }]
    }
});

Then in add a method in your auth service, that will resolve the promise when the user is logged in and reject it when he’s not logged in:

app.factory('Auth', ['$q', '$ionicHistory', '$state', '$location', Auth]);

function Auth($q, $ionicHistory, $state, $location) {
    function requireLogin() {
        return $q(function (resolve, reject) {
            someCheckAuthenticationMethod().then(function (data) {
                // authenticated
                resolve(data); // this will allow the state to change
            }).catch(function () {
                // unauthenticated - initiate login
                reject(); // thanks to this the state will not be changed
                console.log('Return url: ' + $location.path());

                $ionicHistory.nextViewOptions({
                    disableBack: true,
                    historyRoot: true
                });
                
                // go to the login screen and provide return url (important for views others than the dashboard)
                $state.go('app.login', { returnUrl: $location.path() });
            });
        });
    }

    return {
        requireLogin: requireLogin
    };
}

You may not want to have the redirection ($state.go()) logic in the Auth service. In such case you can use it in the resolve function in routing or some other special service for that purpose.

1 Like

You can either use route resolve like @maksm suggested, or use stateChangeSuccess and check for localstorage flag like so:

$rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) ->
  usedAppBefore = localStorageService.getObject('usedAppBefore')
  if (usedAppBefore isnt true)
    localStorageService.setObject('usedAppBefore', true)
    $state.go("App.Walkthrough")
)

In case of authentication something like that

$rootScope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) ->
    if !userService.isLoggedIn()
      event.preventDefault();
      $state.go("App.Tabs.Login", {}, {reload: true})
)

I’m using $urlRouterProvider.otherwise to set my default state. It reads from a localStorage variable that stores my desired default state. Example: http://codepen.io/swaffoja/pen/ZGNEPX

$urlRouterProvider.otherwise(function($injector, $location) {
    var $state = $injector.get('$state')

    var defaultState = window.localStorage['defaultState'] || 'onboarding'        
    $state.go(defaultState)        
})
1 Like

Why make it all so complicated, this is no rocket science.

In my “run” I determine the initial page/state I want the user to see when the app starts, and I do a $state.go(thatState).

Easy.

If you have a code sample I’d love to see it. Using $state.go() in ionicplatform.ready() was the first option in the OP’s list of solutions that didn’t work exactly perfect. I couldn’t make it work perfectly either. My “otherwise” state would appear for a split second before transitioning to my desired state. I’d really prefer to determine a default state there though.

1 Like

I know, but the trick is that if you don’t define a “routeprovider.otherwise” then this initial redirect/flicker does NOT happen. That works perfectly for me.

It’s also just something that I found out by trial and error. But it works.

1 Like

It’s been a while but i’m building another app and tried some suggestions from this thread.
Actually @leob suggestions works perfectly.
The trick is to not define an ‘otherwise’ state and do a $state.go change inside the run function of your app.js.
Don’t put it inside the ready() function because that won’t trigger until you go to a view it seems.

So for a simple solution:

 .run(function ($state) {
    $ionicPlatform.ready(function () {
    }

    if (condition1) {
      $state.go('view1');
    }
    else {
      $state.go('view2');
    }
  });

No split second other view what so ever :smile:

Cool, and yes indeed the second part of the solution is that you do NOT put it inside the “ready” function (I didn’t state that in my answer but that’s what I do as well).

Ionic (and Angular) development is full of these tricks and “gotchas”, the framework is complex but that’s why this forum is invaluable, there are loads of these small tricks to make stuff work in Ionic apps.

(another thing I found out gradually is that the much-praised “route resolves” aren’t that great actually, I’m hardly using them anymore because they’re more trouble than they’re worth)

Works like a charm… KISS ftw

I implemented one for Ionic 2 using local Storage

import { Storage } from '@ionic/storage';
export class App{ 
constructor(public storage:Storage){}
     this.storage.get('launchCount').then(applaunchCount => {
        if (applaunchCount) {
          alert('launch count ' + applaunchCount);
        }
        else {
          alert('launch count '  + applaunchCount);
         this.storage.set('launchCount', applaunchCount);
        }
      });
}

hi @sahildaga95,

I am new to ionic and i’m basically learning as i’m developing. I believe this code goes into the app.component.ts file right? and what I want is to have a loading component in my home page which only loads the first time the app is run. How can i do that in my home.ts? i will appreciate your help. Thanks.

the same code can be used in home.ts as well, since local storage is common throughout your application.You will get the same output in that file which you get in app.component.

1 Like

I don’t get this answer. Could someone explain the 'run". What does it do?

I could use a full snippet for this, as I don’t understand it and have the same “split second” problem.