Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding layout view in higher state from lower state in ui-router results in empty ui-view

I'm trying to create a basic layout system using nested states in angular using ui-router.

What I'd like is that any 'sub state' can override the 'layout' partial of the top level state so that different site layouts can be used depending on the area of the site.

Given the following system/server/views/index.html:

<section data-ui-view="layout" ></section>

and two layouts to be interchanged into the above: system/public/views/layouts/standard.html:

  <div class="navbar navbar-inverse navbar-fixed-top" data-ui-view="header" data-role="navigation"></div>
  <section class="container-fluid">
      <section data-ui-view ></section>
  </section>

system/public/views/layouts/full-width.html:

  <div class="navbar navbar-inverse navbar-fixed-top" data-ui-view="header" data-role="navigation"></div>
  <section class="container">
      <section data-ui-view ></section>
  </section>

sample content system/public/views/index.html

<div>some sample content</div>

and finally the router:

function($stateProvider, $urlRouterProvider) {
  // For unmatched routes:
  $urlRouterProvider.otherwise('/');

  // states for my app
  $stateProvider
    .state('root', {
      url: '/',
      abstract: true,
      views: {
        'layout@': {
          templateUrl: 'system/views/layouts/standard.html'
        },
        'header@root': {
          templateUrl: 'system/views/partials/header.html'
        }
      }
    })
    .state('root.home', {
      url: '',
      views: {
        'header@root': {
          templateUrl: 'system/views/partials/header2.html'
        },
        '': {
          templateUrl: 'system/views/index.html'
        }
      }
    });
}

The above works. I'm able to swap the header in and out in the sub states, but if I add the override for the 'layout':

function($stateProvider, $urlRouterProvider) {
  // For unmatched routes:
  $urlRouterProvider.otherwise('/');

  // states for my app
  $stateProvider
    .state('root', {
      url: '/',
      abstract: true,
      views: {
        'layout@': {
          templateUrl: 'system/views/layouts/standard.html'
        },
        'header@root': {
          templateUrl: 'system/views/partials/header.html'
        }
      }
    })
    .state('root.home', {
      url: '',
      views: {
        'header@root': {
          templateUrl: 'system/views/partials/header2.html'
        },
        '': {
          templateUrl: 'system/views/index.html'
        },
--->>> 'layout@': {
          templateUrl: 'system/views/layouts/full-width.html'
        }
      }
    });
}

When I add the 'layout@' the date-ui-view="layout" does get loaded correctly but the sub ui-views for the header and the unamed do not get replaced.

Any ideas what is going on here?

If this isn't something that should work what are alternative approaches?

like image 214
Trevor Jones Avatar asked Nov 13 '14 06:11

Trevor Jones


1 Answers

The point is that overriding template in a child state 'root.home' like this:

.state('root.home', {
    ...
->> 'layout@': {
      templateUrl: 'system/views/layouts/full-width.html'
    }

Is in fact totally removing any part from the parent 'root'. So now we have to use this for sibling views:

    '[email protected]': {  // see the 'root.home' after @
      templateUrl: 'system/views/partials/header2.html'
    },
    '@root.home': {        // see the 'root.home' after @
      templateUrl: 'system/views/index.html'
    },

See, that we used root.home after @. That is: We are explicitly saying:

  • find the ui-view="header" and unnamed ui-view="" inside of this, current 'root.home' state.

There is no target existing in parent state 'home', because we totally skipped it.

The sample in documentation is in this case really self descriptive:

View Names - Relative vs. Absolute Names

(cited self describing snippet)

$stateProvider
  .state('contacts', {
    // This will get automatically plugged into the unnamed ui-view 
    // of the parent state template. Since this is a top level state, 
    // its parent state template is index.html.
    templateUrl: 'contacts.html'   
  })
  .state('contacts.detail', {
    views: {
        ////////////////////////////////////
        // Relative Targeting             //
        // Targets parent state ui-view's //
        ////////////////////////////////////

        // Relatively targets the 'detail' view in this state's parent state, 'contacts'.
        // <div ui-view='detail'/> within contacts.html
        "detail" : { },            

        // Relatively targets the unnamed view in this state's parent state, 'contacts'.
        // <div ui-view/> within contacts.html
        "" : { }, 

        ///////////////////////////////////////////////////////
        // Absolute Targeting using '@'                      //
        // Targets any view within this state or an ancestor //
        ///////////////////////////////////////////////////////

        // Absolutely targets the 'info' view in this state, 'contacts.detail'.
        // <div ui-view='info'/> within contacts.detail.html
        "[email protected]" : { }

        // Absolutely targets the 'detail' view in the 'contacts' state.
        // <div ui-view='detail'/> within contacts.html
        "detail@contacts" : { }

        // Absolutely targets the unnamed view in parent 'contacts' state.
        // <div ui-view/> within contacts.html
        "@contacts" : { }

        // absolutely targets the 'status' view in root unnamed state.
        // <div ui-view='status'/> within index.html
        "status@" : { }

        // absolutely targets the unnamed view in root unnamed state.
        // <div ui-view/> within index.html
        "@" : { } 
  });

Also, please do notice this important fact:

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.

I.e. - we cannot inherit from parent 'root' state anyhting, because the inheritance goes only view views...

like image 183
Radim Köhler Avatar answered Oct 08 '22 00:10

Radim Köhler