Directing the user to a child state when they are transitioning to its parent state using UI-Router

JavascriptAngularjsAngular Ui-RouterAngular Ui

Javascript 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

  1. First, add a property to the 'manager.staffDetail.view' state of abstract: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.

  2. 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:

  1. User navigates to state “parent”
  2. “$stateChangeStart” event gets fired
  3. Listener for “$stateChangeStart” catches event and passes “toState” (which is “parent”) and “event" to the handler function
  4. Handler function checks if “redirectTo” is set on “toState”.
  5. If “redirectTo” IS NOT set, nothing happening and the user continues on to the “toState” state.
  6. 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”).
  7. 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMeekerView Question on Stackoverflow
Solution 1 - JavascriptTim KindbergView Answer on Stackoverflow
Solution 2 - JavascriptrabView Answer on Stackoverflow
Solution 3 - JavascriptChuck DView Answer on Stackoverflow
Solution 4 - JavascriptnvcnvnView Answer on Stackoverflow
Solution 5 - JavascriptMeekerView Answer on Stackoverflow
Solution 6 - JavascriptsdoxseeView Answer on Stackoverflow
Solution 7 - JavascriptAndyPerlitchView Answer on Stackoverflow
Solution 8 - JavascriptokiganView Answer on Stackoverflow
Solution 9 - JavascriptJonathon HillView Answer on Stackoverflow