State change not working in conjunction with a post


#1

For some reason, I cannot get the state to change when I make a login $http post to my local server.

Controller:

	$scope.login = function(user){
		$http.post('/api/v1/user/login', user)
			.success(function(data, status){
				console.log(data);
				authToken.setToken(data);
				$state.go('user.exercises');
			})
			.error(function(data, status){
				$scope.status = data;
			});
	};

HTML:

<div class="list">
        <label class="item item-input">
            <span class="input-label">Username</span>
            <input type="text" ng-model="user.username">
        </label>
        <label class="item item-input">
            <span class="input-label">Password</span>
            <input type="password" ng-model="user.password">
        </label>
    </div>
    <div class="padding">
        <button class="button button-block button-positive" ng-click="login(user)">
            Log In
        </button>
        <p class="text-center">
            <a href="#/forgot-password">Forgot password</a>
        </p>
    </div>

ionic.project:

"proxies": [
    {
        "path": "/api/v1",
        "proxyUrl": "http://localhost:5000/api/v1"
    }
]

config.xml

<description>
    An Ionic Framework and Cordova project.
</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">
  Ionic Framework Team
</author>
<content src="index.html"/>

//important
<access origin="*"/>
<allow-navigation href="http://*/*" />

<preference name="webviewbounce" value="false"/>
<preference name="UIWebViewBounce" value="false"/>
<preference name="DisallowOverscroll" value="true"/>
<preference name="android-minSdkVersion" value="16"/>
<preference name="BackupWebStorage" value="none"/>
<feature name="StatusBar">
  <param name="ios-package" value="CDVStatusBar" onload="true"/>
</feature>

I’m not sure if it has to do with the way I have it set up to use my server or if it is actually an ionic and ui-router problem. This works with my web version.


#2

Are you running this in your emulator or real device?

Because if you’re not working on your computer localhost:5000 will not work. You can’t use this address outside of your working machine. You will need to use your IP instead.


#3

Now that this post is showing, I’ll post my update. As for that, I’m running it in the browser with ionic serve --lab. I haven’t set up my IP for it to work off my phone, but then I’ll just use the pushed up site.

Also, to be clear, the login does work.

As for my update. It works if I have two $state.go's:

$scope.login = function(user){
  $state.go('user.exercises');
  $state.go('trainer.exercises');
  $http.post('/api/v1/user/login', user)
	.success(function(data, status){
		console.log(data);
		authToken.setToken(data);
		authToken.checkRole().then(function(data) {
			console.log(data.role);
			if(data.role === 'trainer') $state.go('trainer.exercises');
			if(data.role === 'user') $state.go('user.exercises');
		});
	})
	.error(function(data, status){
		console.log(data);
	});
};

The role check and authorize really don’t make a difference. You could take them out and it would still behave the same. The role check just decodes JWT token.

So right now it “works”, but if you have a trainer account you have to hit login twice. I would love to just not have to have two $state.go


#4

Would love the opinion of an Ionic Team Member as this is clearly specific to the Ionic ui-router bundle.


#5

My feeling is the first state change is throwing an exception, add a $stateChangeError handler to catch any exceptions and see if that narrows it down

See https://github.com/angular-ui/ui-router/wiki for info


#6

No $changeStateError, but there is a $changeStateSuccess. Here is the event, toState, toParams, fromState, fromParams in that order:


#7

You can see that it changes state, but doesn’t actually change the view.


#8

Did you remember to define both the parent states before the child states when setting up the routes? Is state user and state trainer are defined?


#9

Here is user. Trainer is the exact same.

.state('user', {
    url: '/user',
    abstract: true,
    templateUrl: './user/views/content.html',
    controller: 'TabController'
})
    .state('user.profile', {
        url: '/profile',
        views: {
            'user-profile': {
                controller: 'UserProfileController',
                templateUrl: './user/views/profile.html'   
            }
        }
    })
    .state('user.exercises', {
        url: '/exercises',
        views: {
            'user-exercises': {
                controller: 'UserExercises',
                templateUrl: './user/views/exercises.html'  
            }
        }
    })
        .state('user.exercise', {
            url: '/exercise',
            views: {
                'user-exercises': {
                    controller: 'UserExercise',
                    templateUrl: './user/views/exercise.html'  
                }
            }
        })
    .state('user.meals', {
        url: '/meals',
        views: {
            'user-meals': {
                controller: 'UserMeals',
                templateUrl: './user/views/meals.html' 
            }
        }
    })

Tab controller is just to check for platform:

function TabController($scope) {
	$scope.platform = ionic.Platform;
}

#10

Most of the stuff there looks okay.

I assume the login is in its own state?

What does your ./user/views/content.html file look like?

I would put a breakpoint at the start of the UserExercises controller and see if the controller gets initialized. If it does not get that far then there is a problem in the router initialization of the state.

I would also check the status code and make sure it is 200, there are other codes that will cause success to be called but not 200.

I would also take out the http call entirely and just call $state.go(‘user.exercises’) in the login function and see if that works.

Failing that I would create a new project, add the user route and the user exercises route and the login route and get that working - make what you have as simple as possible then post it to codepen or possibly email it me and I can have a look.

I do this stuff all the time and it should be trivial to fix. All the time I have had the $stateChangeError be the most useful for tracking down these types of errors.

Paul


#11

Yes, the login is it’s own state and here is what ./user/views/content.html looks like:

<ion-tabs class="tabs-icon-top tabs-color-active-positive">

<!-- Dashboard Tab -->
<ion-tab title="Dashboard" icon-off="ion-speedometer" icon-on="ion-speedometer" href="#/user/dashboard">
    <ion-nav-view name="user-dashboard"></ion-nav-view>
</ion-tab>

<!-- Programs Tab -->
<!-- android -->
<ion-tab ng-if="platform.isAndroid()" title="Programs" icon-off="ion-android-list" icon-on="ion-android-list" href="#/user/programs">
    <ion-nav-view name="user-programs"></ion-nav-view>
</ion-tab>
<!-- ios -->
<ion-tab ng-if="platform.isIOS()" title="Programs" icon-off="ion-ios-list-outline" icon-on="ion-ios-list" href="#/user/programs">
    <ion-nav-view name="user-programs"></ion-nav-view>
</ion-tab>

<!-- Exercises Tab -->
<!-- android -->
<ion-tab ng-if="platform.isAndroid()" title="Exercises" icon-off="ion-android-clipboard" icon-on="ion-android-clipboard" href="#/user/exercises">
    <ion-nav-view name="user-exercises"></ion-nav-view>
</ion-tab>
<!-- ios -->
<ion-tab ng-if="platform.isIOS()" title="Exercises" icon-off="ion-clipboard" icon-on="ion-clipboard" href="#/user/exercises">
    <ion-nav-view name="user-exercises"></ion-nav-view>
</ion-tab>

<!-- Meals Tab -->
<!-- android -->
<ion-tab ng-if="platform.isAndroid()" title="Meals" icon-off="ion-fork" icon-on="ion-fork" href="#/user/meals">
    <ion-nav-view name="user-meals"></ion-nav-view>
</ion-tab>
<!-- ios -->
<ion-tab ng-if="platform.isIOS()" title="Meals" icon-off="ion-fork" icon-on="ion-fork" href="#/user/meals">
    <ion-nav-view name="user-meals"></ion-nav-view>
</ion-tab>

<!-- Profile Tab -->
<!-- android -->
<ion-tab ng-if="platform.isAndroid()" title="Profile" icon-off="ion-person" icon-on="ion-person" href="#/user/profile">
    <ion-nav-view name="user-profile"></ion-nav-view>
</ion-tab>
<!-- ios -->
<ion-tab ng-if="platform.isIOS()" title="Profile" icon-off="ion-person" icon-on="ion-person" href="#/user/profile">
    <ion-nav-view name="user-profile"></ion-nav-view>
</ion-tab>

<!-- Messaging Tab -->
<!-- android -->
<ion-tab ng-if="platform.isAndroid()" title="Message" icon-off="ion-android-chat" icon-on="ion-android-chat" href="#/user/message">
    <ion-nav-view name="user-message"></ion-nav-view>
</ion-tab>
<!-- ios -->
<ion-tab ng-if="platform.isIOS()" title="Message" icon-off="ion-ios-chatboxes-outline" icon-on="ion-ios-chatboxes" href="#/user/message">
    <ion-nav-view name="user-message"></ion-nav-view>
</ion-tab>


</ion-tabs>

The UserExercise controller gets initiated because I can see that it logs some data from a function inside of it.

I will check on the status code, but I doubt it’s anything else because it’s no different that what I use on the web version.

If I take out the $http.post it works as intended.

I probably will have to make a new project and try that. Unfortunately, I don’t get a $stateChangeError, it actually is a $stateChangeSuccess.

If I take out the title from the <ion-view> on the login page, you can see that after I click login, the title switches to “Exercises” but the view inside the <ion-view> doesn’t change.


#12

And it looks like I found the source of the problem. It has to do with the ng-if="platform.isAndroid/IOS" which is causing the views not to change entirely.