Nested ui-router state without nested view?

All we need is an easy explanation of the problem, so here it is.

I’m wondering if it’s possible to have a nested state without a nested view. Suppose I have this setup:

App.config(function($stateProvider, $urlRouterProvider) {
    //
    //
    // Now set up the states
    $stateProvider
    .state('index', {
        url: "/index",
       templateUrl: "views/home.html",
        controller: "MainController",
        ncyBreadcrumb: {
            label: 'Home'
        }
    })
    .state('About', {
        url: "/about",
        templateUrl: "views/about.html",
        controller: "AboutController",
        ncyBreadcrumb: {
            label: 'About',
            parent: 'index'
        }
    })
    .state('us', {
        url: "/us",
        templateUrl: "views/us.html",
        controller: "UsController",
        parent: 'about',
        ncyBreadcrumb: {
            label: 'Us'
        }
    })
    //
    // For any unmatched url, redirect to /home
    $urlRouterProvider.otherwise("/index");

});

When I visit /about, I get the about page. When I visit /about/us, I still get the about page with the us page loaded in the ui-view of the about page. However, what I would like to do is load the about page on /about and just the us page on /us. Is this possible?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Yes it is possible. What we have to use, is the absolute naming. State defintion would look like this:

.state('us', {
    url: "/us",
    views : {
      "@" : { // here we are using absolute name targeting
        templateUrl: "views/us.html",
        controller: "UsController", 
      },
    }
    parent: 'about',
    ncyBreadcrumb: {
        label: 'Us'
    }
})

See doc:

View Names – Relative vs. Absolute Names

Behind the scenes, every view gets assigned an absolute name that follows a scheme of [email protected], where viewname is the name used in the view directive and state name is the state’s absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.

For example, the previous example could also be written as:

.state('report',{
    views: {
      '[email protected]': { },
      '[email protected]': { },
      '[email protected]': { }
    }
})

So as documentation shows, we can use absolute naming. In our case, we will target root state which nams is string empty (index.html) – the part after delimiter @. And it is unnamed view – string Empty before @. That is why we use:

    views : {
      "@" : {

NOTE: behind the scenes, UI-Router used this for state us:

    views : {
      "@about" : {

There is a working plunker, with these states in action:

// States
$stateProvider

  .state('index', {
    url: "/index",
    templateUrl: 'tpl.html',
  })
  .state('about', {
    url: "/about",
    templateUrl: 'tpl.html',
  })
  .state('us', {
    url: "/us",
    parent: "about",
    views : {
      '@': {
        templateUrl: 'tpl.html',
      },
    }
  })

Check it in action that if the ‘us’ is a state name the ui-sref="us" will properly navigate to '/about/us'.

Method 2

Sure, just because a state shares part of the url doesn’t mean it has to be a parent/child relationship. Just set the us state’s url to /about/us and don’t give it a parent.

App.config(function($stateProvider, $urlRouterProvider) {
    //
    //
    // Now set up the states
    $stateProvider
    .state('index', {
        url: "/index",
        templateUrl: "views/home.html",
        controller: "MainController",
        ncyBreadcrumb: {
            label: 'Home'
        }
    })
    .state('About', {
        url: "/about",
        templateUrl: "views/about.html",
        controller: "AboutController",
        ncyBreadcrumb: {
            label: 'About',
            parent: 'index'
        }
    })
    .state('us', {
        url: "/about/us",
        templateUrl: "views/us.html",
        controller: "UsController",
        ncyBreadcrumb: {
            label: 'Us'
        }
    })
    //
    // For any unmatched url, redirect to /home
    $urlRouterProvider.otherwise("/index");
});

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply