Angular ui.router. Deep nested routes

3.2k views Asked by At

Here is an example to check http://embed.plnkr.co/uVMlkk/preview

When we navigate to 'page2' route there is a 'hey, I'm a subroute' note. But once we navigate anywhere else that note will disappear forever.

The goal is to make some nested states to be shown right away (as a default ones).

I assume there should be some cases using $state.go(), but can't figure it out so far. Any help is highly appreciated.

State definition snippet:

  .state('root.page2.tab', {
    url: '/:tabId',
    templateUrl: 'tpl.page2.tab.html',
    controller: 'Page2TabController'
  })

  .state('root.page2.tab.subroute', {
    url: '',
    templateUrl: 'tpl.page2.tab.subroute.html'
  })

the content of the 'tpl.page2.tab.subroute.html':

hey, I'm a subroute

related controller:

  .controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
    $scope.tabId = $state.params.tabId;
    $state.go('root.page2.tab.subroute');
  }])
2

There are 2 answers

2
Radim Köhler On BEST ANSWER

There is a trick how to handle scenarios:

Parent should trigger some action in case that

  • it is accessed, or
  • its reached again, when navigating back from child in a parent state

In that case, we can use the "target (ui-view) for a child" as a place where sits the special view, with special controller. This will be

  • injected into that position once parent is created and
  • re-injected into that position again, once child is left. In that case, it will be re-init.

Enough explanation. There is a working plunker. There is adjusted state:

.state('root.page2', {
    url: '/page2',
    views: {
      'content@root': {
        templateUrl: './tpl.page2.html',
        controller: 'Page2Controller'
      },
      '@root.page2': {
        template: '<div></div>',
        controller: 'RedirectorController'
      }
    }
})

So, now we can do some magic inside of our 'RedirectorController'

.controller('RedirectorController', ['$scope', '$state', 
   function($scope, $state) {
      $state.go('root.page2.tab', { tabId: $scope.activeTabId });
}])

Check it in action here

Read more about what that new view/controller get from the other (Scope Inheritance by View Hierarchy Only) one here

5
Radim Köhler On

There is a fixed version.

I removed the url from the 'root.page2.tab.subroute'

.state('root.page2.tab.subroute', {
    //url: '',
    templateUrl: 'tpl.page2.tab.subroute.html'
})

And because the parent has defined paramater tabId:

.state('root.page2.tab', {
    url: '/:tabId',
    templateUrl: 'tpl.page2.tab.html',
    controller: 'Page2TabController'
})

We have to pass that param inside of the redicrection:

.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
    $scope.tabId = $state.params.tabId;
    // instead of this
    // $state.go('root.page2.tab.subroute');
    // we need this
    $state.go('root.page2.tab.subroute', $state.params);
 }])

Check the working, fixed version here

ANOTHER approach - using redirectTo - there is a working plunker

One way, inspired by this:

Redirect a state to default substate with UI-Router in AngularJS

could be to add a very smart but small redirect code snippet:

.run(['$rootScope', '$state', function($rootScope, $state) {
    $rootScope.$on('$stateChangeStart', function(evt, to, params) {
      if (to.redirectTo) {
        evt.preventDefault();
        $state.go(to.redirectTo, params)
      }
    });
}])

And adjust our state like this:

.state('root.page2.tab', {
    url: '/:tabId',
    templateUrl: 'tpl.page2.tab.html',
    controller: 'Page2TabController',
    redirectTo: 'root.page2.tab.subroute',
})

Check it here