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:
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.
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.
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();
});
}
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.
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.
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)
})
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.
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.
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.
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)
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.