Nested states or views for layout with leftbar in ui-router?

I have the following layout:

enter image description here

Sidebar and Headerbar will always be present though their content is context-specific.

I think there are two options here: nested states (sidenav > Headerbar > Content) or with views (if I understand that correctly). I'm still struggling to get my head wrapped around ui-router regardless of how many videos and articles I've read.

Clicking on Sidenav would load a state (or view) into Content and Headerbar would adjust its content based on whatever is loaded into Content.

My sense is that nested states seem like the simplest direct approach, particularly when thinking about inheritance.

Looking at it from another point of view, these seem like they could be siblings (although inheritance issues probably make me wrong). My inkling is that views would allow me more flexibility in the future with sub-items and such.

And of course ng-include and directives could play into this.

Being new to ui-router could someone slap me in the right direction? Where I'm stuck is loading the home view. I want my users to see their dashboard in the Content section once they log in. And then, how do I load new elements into Content as the user navigates from the Sidebar?


Solution 1:

One way how to design scenario with 1) side bar, 2) action section and 3) main area could be like in this working example

Firstly the root state. Here is root state named 'index'. It is abstract and could do some resolve for us. It does not effect child state naming and does not extend the url (because is undefined)

$stateProvider
    .state('index', {
        abstract: true,
        //url: '/',
        views: {
          '@' : {
            templateUrl: 'layout.html',
            controller: 'IndexCtrl'
          },
          'top@index' : { templateUrl: 'tpl.top.html',},
          'left@index' : { templateUrl: 'tpl.left.html',},
          'main@index' : { templateUrl: 'tpl.main.html',},
        },
      })

The first real state is list, and it inherits from parent but with an attribute parent: 'index', so the parent name is not effecting the state name.

Advantage is, that it could inherit lot of resolved stuff. Also, the root state could be loaded once, for all other parent states

    .state('list', {
        parent: 'index',
        url: '/list',
        templateUrl: 'list.html',
        controller: 'ListCtrl'
      })

This is the real power of UI-Router, because now we can see that child is injecting stuff into two places - 1) action section and 2) main area

    .state('list.detail', {
        url: '/:id',
        views: {
          'detail@index' : {
            templateUrl: 'detail.html',
            controller: 'DetailCtrl'
          },
          'actions@index' : {
            templateUrl: 'actions.html',
            controller: 'ActionCtrl'
          },
        },
      })

This way, we can use named views and multi views in real world scenario. Please, never forget how the scope definition goes:

Scope Inheritance by View Hierarchy Only

Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).

It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.

Check that all in action here

Solution 2:

I just would like to share my experience. There is

  • similar Q & A: Angular UI Router - Nested States with multiple layouts
  • and a link to working plunker

The snippet of the state def:

$stateProvider
    .state('index', {
        url: '/',
        views: {
          '@' : {
            templateUrl: 'layout.html',
            controller: 'IndexCtrl'
          },
          'top@index' : { templateUrl: 'tpl.top.html',},
          'left@index' : { templateUrl: 'tpl.left.html',},
          'main@index' : { templateUrl: 'tpl.main.html',},
        },
      })
    .state('index.list', {
        url: '/list',
        templateUrl: 'list.html',
        controller: 'ListCtrl'
      })
    .state('index.list.detail', {
        url: '/:id',
        views: {
          'detail@index' : {
            templateUrl: 'detail.html',
            controller: 'DetailCtrl'
          },
        }

In a nutshell, I do use the nesting approach.

It is similar to the "core example" available here http://angular-ui.github.io/ui-router/sample/#/. It is hierarchical (entity list / detail)

And what's more, I use the hidden supper root state:

  • check the details here Updating resolved objects in ui.router parent states
  • the examle link

which is handling security related stuff - once, and shared among all child states:

$stateProvider
  .state('root', {
    abstract: true,
    template: '<div ui-view></div>',
    resolve: {objectX : function() { return {x : 'x', y : 'y'};}},
    controller: 'rootController',
  })
  .state('home', {
    parent: "root",
    url: '/home',
    templateUrl: 'tpl.example.html',
  })
  .state('search', {
    parent: "root",
    url: '/search',
    templateUrl: 'tpl.example.html',
  })

Hope it does enlighten this a bit, because the power of UI-Router I see in multiviews, view nesting, scope inheritance and the logical state machine behind