Directing the user to a child state when they are transitioning to its parent state using UI-Router
JavascriptAngularjsAngular Ui-RouterAngular UiJavascript Problem Overview
Consider the following:
.state('manager.staffList', {url:'^/staff?alpha', templateUrl: 'views/staff.list.html', data:{activeMenu: 'staff'}, controller: 'staffListCtrl'})
.state('manager.staffDetail', {url:'^/staff/{id}' , templateUrl: 'views/staff.html', data:{activeMenu: 'staff'}, controller: 'staffDetailsCtrl'})
.state('manager.staffDetail.view', {url:'/view', templateUrl: 'views/staff.details.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.schedule', {url:'/schedule', templateUrl:'views/staff.view.schedule.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.history', {url:'/history' , templateUrl:'views/staff.view.history.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.log', {url:'/log', templateUrl:'views/staff.view.log.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.files', {url:'/files', templateUrl:'views/staff.view.files.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.edit', {url:'/edit', templateUrl: 'views/staff.edit.html', data:{activeMenu: 'staff'}})
If I go to domain.com/staff/1234/view
, how do I default to the manager.staffDetail.view.schedule
child state?
Javascript Solutions
Solution 1 - Javascript
-
First, add a property to the
'manager.staffDetail.view'
state ofabstract:true
. This isn't required, but you want to set this since you'd never go to this state directly, you'd always go to one of it's child states. -
Then do one of the following:
-
Give the
'manager.staffDetail.view.schedule'
state an empty URL. This will make it match the same url as it's parent state url, because it appends nothing to the parent url.`.state('manager.staffDetail.view.schedule', {url:'', ...`
-
Or if you want to keep the url of the default child route unchanged, then set up a redirect in your module.config. This code here will redirect any location of
'/staff/{id}/view'
to the location of'/staff/{id}/view/schedule'
:`$urlRouterProvider.when('/staff/{id}/view', '/staff/{id}/view/schedule');`
Solution 2 - Javascript
For setting default child view , check this example . On clicking Route 1
load default state route1.list
// For any unmatched url, send to /route1
$stateProvider
.state('route1', {
url: "/route1",
abstract:true ,
templateUrl: "route1.html"
})
.state('route1.list', {
url: '',
templateUrl: "route1.list.html",
controller: function($scope){
$scope.items = ["A", "List", "Of", "Items"];
}
})
.state('route1.desc', {
url: "/desc",
templateUrl: "route1.desc.html",
controller: function($scope){
$scope.items = [];
}
})
.state('route2', {
url: "/route2",
templateUrl: "route2.html"
})
.state('route2.list', {
url: "/list",
templateUrl: "route2.list.html",
controller: function($scope){
$scope.things = ["A", "Set", "Of", "Things"];
}
})
Solution 3 - Javascript
I ran into the same issue and found this solution to work:
https://github.com/angular-ui/ui-router/issues/948#issuecomment-75342784
This is quoted from @christopherthielen on github
> "For now, don't declare your state abstract, and use this recipe:"
app.run($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(evt, to, params) {
if (to.redirectTo) {
evt.preventDefault();
$state.go(to.redirectTo, params)
}
});
}
$stateProvider.state('parent' , {
url: "/parent",
templateUrl: "parent.html",
redirectTo: 'parent.child'
});
$stateProvider.state('parent.child' , {
url: "/child",
templateUrl: "child.html"
});
Here is breakdown of how the process works:
- User navigates to state “parent”
- “$stateChangeStart” event gets fired
- Listener for “$stateChangeStart” catches event and passes “toState” (which is “parent”) and “event" to the handler function
- Handler function checks if “redirectTo” is set on “toState”.
- If “redirectTo” IS NOT set, nothing happening and the user continues on to the “toState” state.
- If “redirectTo" IS set, the event is canceled (event.preventDefault) and $state.go(toState.redirectTo) sends them to the state specified in “redirectTo” (which is “parent.child”).
- The “$stateChangeStart” event gets fired again, but this time “toState” == “parent.child” and the “redirectTo” option is not set, so it continues to “toState”.
Solution 4 - Javascript
Quite late but I think you can "redirect" to the state you want.
.state('manager.staffDetail.view', {
url:'/view',
templateUrl: 'views/staff.details.html',
controller: 'YourController'
})
app.controller('YourController', ['$state',
function($state) {
$state.go('manager.staffDetail.view.schedule');
}]);
You can write you controller right in the state config for short.
Solution 5 - Javascript
I changed 'manager.staffDetial.view' to an abstract state and left the url of my default child state to blank ''
// Staff
.state('manager.staffList', {url:'^/staff?alpha', templateUrl: 'views/staff.list.html', data:{activeMenu: 'staff'}, controller: 'staffListCtrl'})
.state('manager.staffDetail', {url:'^/staff/{id}', templateUrl: 'views/staff.html', data:{activeMenu: 'staff'}, controller: 'staffDetailsCtrl'})
.state('manager.staffDetail.view', {url:'/view', abstract: true, templateUrl: 'views/staff.details.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.schedule', {url:'', templateUrl:'views/staff.view.schedule.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.history', {url:'/history', templateUrl:'views/staff.view.history.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.log', {url:'/log', templateUrl:'views/staff.view.log.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.files', {url:'/files', templateUrl:'views/staff.view.files.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.edit', {url:'/edit', templateUrl: 'views/staff.edit.html', data:{activeMenu: 'staff'}})
Solution 6 - Javascript
In "angular-ui-router": "0.2.13", I don't think @nfiniteloop's redirect solution will work. It worked once I had rolled back to to 0.2.12 (and may have had to put the $urlRouterProvider.when call before the $stateProvider?)
see https://stackoverflow.com/a/26285671/1098564
and https://stackoverflow.com/a/27131114/1098564 for a workaround (if you don't want to go back to 0.2.12)
as of Nov 28 2014, https://github.com/angular-ui/ui-router/issues/1584 indicates that it should work again in 0.2.14
Solution 7 - Javascript
This is late but all the answers here don't apply to the latest version of angular-ui-router for AngularJS. As of that version (specifically @uirouter/angularjs#v1.0.x you can just put redirectTo: 'childStateName'
in the second param of $stateProvider.state()
. For example:
$stateProvider
.state('parent', {
resolve: {...},
redirectTo: 'parent.defaultChild'
})
.state('parent.defaultChild', {...})
Here is the relevant doc section: https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-hook-redirectto
Hopefully this helps someone!
Solution 8 - Javascript
here is a very simple and transparent alternative by just modifying parent state in ui router:
.state('parent_state', {
url: '/something/:param1/:param2',
templateUrl: 'partials/something/parent_view.html', // <- important
controller: function($state, $stateParams){
var params = angular.copy($state.params);
if (params['param3'] === undefined) {
params['param3'] = 'default_param3';
}
$state.go('parent_state.child', params)
}
})
.state('parent_state.child', {
url: '/something/:param1/:param2/:param3',
templateUrl: '....',
controller: '....'
})
Solution 9 - Javascript
Add angular-ui-router-default and add the abstract
and default
options to the parent state:
...
.state('manager.staffDetail.view', {abstract: true, default: '.schedule', url:'/view', templateUrl: 'views/staff.details.html', data:{activeMenu: 'staff'}})
.state('manager.staffDetail.view.schedule', {url:'/schedule', templateUrl:'views/staff.view.schedule.html', data:{activeMenu: 'staff'}})
...
Note: for this to work, the parent template must have <ui-view/>
somewhere in it.