Issue with $state.go() transitions, only on second click - angularjs

I have a issue with ui-router which I can't quite put my finger on. I can click on a link (on the login page of my app) and navigate to a second page (one where the user can register). There is a cancel button on the second page which simply navigates the user back to the first page. All good so far.
If I then click on the same link again, the user appears to stay on the same page (i.e. the login page), however I have added a 'state change' handler to my app (it just logs the transition to the console) and can see that what actually happens is that the app seems to transition to the second page and then back again. The following won't make much sense other than to demonstrate the flow. You can see the state change start, end and the view load before it starts again and transitions back to 'authentication.login'
Start state change : {authentication.login} to {authentication.registration}
View content loading
End state change success : {authentication.login} to {authentication.registration}
View content loaded
Start state change : {authentication.registration} to {authentication.login}
View content loading
End state change success : {authentication.registration} to {authentication.login}
View content loaded
NOTE: both pages are siblings of the same abstract node.
Any ideas why this might be happening?
NOTE: state config is as follows
.state("authentication", {
url : "/auth",
abstract : "true",
templateUrl : "app/main/common/html/templates/simple-template.html",
})
.state("authentication.login", {
url: "/login",
views: {
"main": {
templateUrl : "app/main/authentication/html/login.html",
controller: "LoginController"
}
}
})
.state("authentication.registration", {
url: "/registration",
views: {
"main": {
templateUrl : "app/main/authentication/html/registration.html",
controller: "RegistrationController"
}
}
})

do you have any
rootScope.$on('$stateChangeStart'
logic in your app that is causing the state transition.
Usually logic will be put in here to kick a user back to the login page if the user is not auth'd
a wild guess but the best guess i can make given the information provided.
thanks
another idea, if you remove the state change handler does everything work ok?

Related

ui-router doesn't change browser url with default params

I actually have two problems.
Here is a state:
$stateProvider.state('detail', {
url: '/detail?query',
params: {query: 'abc'}
templateUrl: 'views/detail.html',
controller: 'DetailCtrl'
})
If I go to /detail, $stateParams.query is indeed set to abc. However, browser URL is still /detail, rather than /detail?query=abc (what we want).
What we currently do is call $location.search('query', 'abc') when DetailCtrl loads. However, when back button is clicked at /detail?query=abc, the browser goes back to /detail.
Expected behavior:
Go to /detail, browser should automatically set URL to /detail?query=abc.
Click back button at /detail?query=abc, browser should go back to the page before /detail.

Override back button to go to specific state using Angular UI Router

I have an Angular app with various different states defined. I do not want the user to be able to go back to previous states using the back button, as they are part of a simulation and it disturbs the simulation flow.
Is there a way I can override the browser back button to take users to the starting state of the simulation?
angular.module('participant').config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('simulations', {
url: '/',
controller: 'SimulationIndexController',
templateUrl: '/angular_templates/simulations/index.html',
})
.state('simulation.content', {
// other routes
});
I want to go to the 'simulations' state when the user presses the back button on the browser but I am not sure how to accomplish this.
Use location: 'replace' when doing state transitions:
$state.go("simulation.content.state2",{location:'replace'});
By using the replace option, the individual simulation steps will not be stored in the browser history. When the user hits the back button, the user will be taken back to the beginning instead of the previous content.
You could add on top
.run(function($state) {
$(window).on('popstate', function(event) {
// This event fires on current history change
$state.go("simulations");
}, false);
})

Ionic / UI router: Navigating to the same state with reload: true breaks the history

I'm trying to make my ionic app transition within the same state but with different URL. I have a the following state:
.state('app.profile.id', {
url: '/profile/:id',
data : {
id: -1
},
views: {
'menuContent': {
templateUrl: 'templates/pages/profile.html',
controller: 'ProfileCtrl'
}
}
})
This is the profile page of a user. What I'm trying to do is to have a link from the profile page to other users profile pages. I'm making the transition with the following code:
$state.go('app.profile.id', {id: id}, {reload: true});
If I don't add the reload: true part, then the data on the page won't refresh since I'm not changing the state. What I understand from reload: true is that it forces transition between states.
However my problem is, the back button acts weird after the transition. The back transition doesn't happen at first few levels: It changes the id (which is noticeable after a manual refresh), and after one or two level, $ionicHistory.goBack() breaks completely.
I think this is a bug in UI router, any idea how to fix this ?
Thanks in advance.

Create a non abstract parent with a URL

I have a simple form that can be used to initiate a forecast request. I created this as a parent state requests (Initiate Forecast).
Desired behavior
When a request is submitted, that immediate request is shown in a child state (View Status) as most recent request. This View Status state will also hold a grid of all past requests, meaning I will be refreshing the grid with data every time this state is invoked.
Both parent and child states are navigable from a sidebar menu.
So, if a user clicks on parent (Initiate Forecast), he should be able to see only the form to submit a request. If a user directly clicks on the 'View Status'(child), then he should be able to see both the form and the grid of requests.
app.js
function statesCallback($stateProvider) {
$stateProvider
.state('requests', {
url: '',
views: {
'header': {
templateUrl: 'initiateforecasting.html',
controller: 'requestsInitiateController'
},
'content': {
template: '<div ui-view></div>'
}
},
params: {
fcId: null,
fcIndex: null
}
})
.state('requests.viewStatus', {
url: '/ViewStatus',
templateUrl: 'viewstatus.html',
controller: 'requestsStatusController'
});
}
var requestsApp = angular.module('requestsApp', ['ui.router']);
requestsApp.config(['$stateProvider', statesCallback]);
requestsApp.run(['$state', function($state) {
$state.go('requests');
}]);
Plunker of my attempts so far.
This is all working for me as shown in the plunker, but I am doing it by not setting a URL to the parent. Having no URL for the parent state is allowing me to see both states. If I add a URL to parent state, then clicking on View Status link is not going anywhere (it stays on Initiate).
How do I change this so that I can have a URL for parent state and still retain the behaviour I need from the parent and child states as described above?
Note: I am fine without the URL for parent state in standalone sample code, but when I integrate this piece with backend code, having no URL fragment on the parent state is making an unnecessary request to the server. This is visible when I navigate to the child state and then go to the parent state. It effectively gives the impression of reloading the page which I think is unnecessary and can be avoided if a URL can be set to the parent state.
You shall not directly write url when using ui.router, try like this:
<a ui-sref="requests.viewStatus">View Status</a>
You are writing state name in ui-sref directive and it automatically resolves url. It's very comfortable because you can change urls any time and it will not break navigation.

ui-router navigation is occurring twice under certain conditions

I have a UI-Router site with the following states:
$stateProvider
.state('order', {
url: '/order/:serviceId',
abstract:true,
controller: 'OrderController'
})
.state('order.index', {
url:'',
controller: 'order-IndexController'
})
.state('order.settings', {
url:'',
controller: 'order-SettingsController'
})
Where my two states do NOT have a url set, meaning they should only be reachable through interaction with the application. However the order.index state is automatically loaded by default because of the order in which I have defined the states.
I am noticing that trying to do a ui-sref="^.order.settings" or $state.go("^.order.settings") then ui-router first navigates to the order.settings state and then it immediately navigates to the order.index state (default state). I think this is happening because the url changing is causing a second navigation to occur and since the state.url == '' for both, it automatically defaults to the order.index state...
I tested this out by setting the {location: false} object in the $state.go('^order.settings', null, {location:false}). This caused the correct state to load (order.settings), but the url did not change. Therefore I think the url changing is triggering a second navigation.
I'd understand, can imagine, that you do not like my answer, but:
DO NOT use two non-abstract states with same url defintion
This is not expected, therefore hardly supported:
.state('order.index', {
url:'', // the same url ...
...
})
.state('order.settings', {
url:'', // .. used twice
...
})
And while the UI-Router is really not requiring url definition at all (see How not to change url when show 404 error page with ui-router), that does not imply, that 2 url could be defined same. In such case, the first will always be evaluated, unless some special actions are used...
I would strongly sugest, provide one of (non default) state with some special url
.state('order.settings', {
url:'/settings', // ALWAYS clearly defined
...
})

Resources