Angular UI Router - Nested States with multiple layouts

I want to have multiple layouts (1-col, 2-col, 3-col) which I want to switch out for different routes based on the layout needed.

I currently have a root state which defaults to using a certain layout and then within that layout contains named ui-view directives for sections such as the header, footer, sidebar etc.

I was just wondering if it is possible to change out the layout for nested states, as it currently does not work and no errors within the console or linter are appearing.

I am currently getting no output in the browser at all which makes me think I have implemented the approach wrong, as all routes aren't inheriting from the route state.

Here's the code:

My-module.js

'use strict';

angular.module('my-module', ['ngResource', 'ui.router'])
  // Routing for the app.
  .config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('root', {
        url: '',
        views: {
          'layout': {
            templateUrl: 'partials/layout/1-column.html'
          },
          'header': {
            templateUrl: 'partials/layout/sections/header.html'
          },
          'footer': {
            templateUrl: 'partials/layout/sections/footer.html'
          }
        }
      })
      .state('root.home', {
        url: '/'
      })
      .state('root.signup', {
        url: '/signup',
        views: {
          'step-breadcrumb': {
            templateUrl: 'partials/signup/step-breadcrumb.html'
          }
        }
      })
      ;

    $urlRouterProvider.otherwise('/');
  })
  ;

Index.html

<!doctype html>
<html class="no-js" ng-app="my-module">
  <head>
    <meta charset="utf-8">
    <title>My Test App</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">

    <!-- build:css({.tmp,app}) styles/main.css -->
    <link rel="stylesheet" href="styles/main.css">
    <!-- endbuild -->

    <!-- build:js scripts/modernizr.js -->
    <script src="bower_components/modernizr/modernizr.js"></script>
    <!-- endbuild -->
  </head>
  <body>

    <div ui-view="layout">
    </div>

    <!-- build:js({app,.tmp}) scripts/vendor.js -->
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/angular-resource/angular-resource.js"></script>
    <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <!-- endbuild -->

    <script src="scripts/config.js"></script>

    <!-- build:js({app,.tmp}) scripts/main.js -->
    <script src="scripts/my-module.js"></script>
    <!-- inject:partials -->
    <!-- endinject -->
    <!-- endbuild -->

  </body>
</html>

1-column.html

<div class="one-column">

  <!-- page header -->
  <div ui-view="header">
  </div>
  <!-- / page header -->

  <!-- state breadcrumb (optional) -->
  <div ui-view="step-breadcrumb">
  </div>
  <!-- / state breadcrumb -->

  <!-- page-container -->
  <div class="page-container">

    <!-- main content -->
    <div ui-view="main-content">
    </div>
    <!-- / main content -->

  </div>
  <!-- / page-container -->

  <!-- page footer -->
  <div ui-view="footer">
  </div>
  <!-- / page footer -->

</div>

Appreciate the help


While there is no plunker showing your example, I will share one with you: working layout example. Please observe it to see in action what I will try to explain here

If our root state should be the only root state, injected into index.html, we do need a bit different defintion:

So for index.html

// index.html
<body>
    <div ui-view="layout">
    </div>
</body>

we would need this syntax:

$stateProvider
  .state('root', {
    url: '',
    views: {
      'layout': {
        templateUrl: 'partials/layout/1-column.html'
      },
      'header@root': {
        templateUrl: 'partials/layout/sections/header.html'
      },
      'footer@root': {
        templateUrl: 'partials/layout/sections/footer.html'
      }
    }
  })

What is this: 'header@root'? it is absolute naming. Check it here:

  • View Names - Relative vs. Absolute Names (small cite below)
  • and here I tried to expain that in detail

Behind the scenes, every view gets assigned an absolute name that follows a scheme of 'viewname@statename', 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.

The same goes for other states definitions. Because the 'root.signup' has direct parent 'root', the existing syntax would be working

.state('root.signup', {
    url: '/signup',
    views: {
      'step-breadcrumb': { // working AS IS
      ...

But we could use absolute naming and it would work as well

.state('root.signup', {
    url: '/signup',
    views: {
      'step-breadcrumb@root': { // absolute naming
      ...

because this is how it works behind anyway.

Please, observe the layout example for more details