Skip to content

Multiple Named Views

Tim Kindberg edited this page Jul 27, 2013 · 48 revisions

◄ Back (Nested States & Nested Views)     Next (URL Routing) ►

You can name your views so that you can have more than one ui-view per template. Let's say you had an application state that needed to dynamically populate a graph, some table data and filters for the table like this:

Multiple Named Views Mockup

When setting multiple views you need to use the views property on state. views is an object.

Views override state's template properties

If you define a views object, your state's templateUrl, template and templateProvider will be ignored. So in the case that you need a parent layout of these views, you can define an abstract state that contain a template, and a child state under the layout state that contain the 'views' object.

Example - Name Matching

The property keys on views should match your view names, like so:

<!-- index.html -->
<body>
  <div ui-view="filters"></div>
  <div ui-view="tabledata"></div>
  <div ui-view="graph"></div>
</body>
$stateProvider
  .state('report', {
    views: {
      'filters': { ... templates, controllers, resolve, etc ... },
      'tabledata': {},
      'graph': {},
    }
  })

Then each view in views can set up its own templates, controllers, and resolve data.

$stateProvider
  .state('report',{
    views: {
      'filters': {
        templateUrl: 'report-filters.html',
        controller: function($scope){ ... controller stuff just for filters view ... }
      },
      'tabledata': {
        templateUrl: 'report-table.html',
        controller: function($scope){ ... controller stuff just for tabledata view ... }
      },
      'graph': {
        templateUrl: 'report-graph.html',
        controller: function($scope){ ... controller stuff just for graph view ... }
      },
    }
  })

View Names - Relative vs. Absolute Names

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.

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

  .state('report',{
    views: {
      'filters@': { },
      'tabledata@': { },
      'graph@': { }

Notice that the view name is now specified as the absolute name, as opposed to the relative name. It is targeting the 'filters', 'tabledata', and 'graph' views located in the root unnamed view. The root unnamed view is your index.html. This lets us do some powerful view targeting. Let's assume we had several nested views set up like this (this example is not realistic, its just to illustrate view targeting):

<!-- index.html (root unnamed view) -->
<body ng-app>
<div ui-view></div> <!-- contacts.html plugs in here -->
<div ui-view="status"></div>
</body>
<!-- contacts.html -->
<h1>My Contacts</h1>
<div ui-view></div>
<div ui-view="detail"></div>
<!-- contacts.detail.html -->
<h1>Contacts Details</h1>
<div ui-view="info"></div>

Let's look at the various views you could target from within the contacts.detail state. Remember that if an @ is used then the view path is considered absolute:

$stateProvider
  .state('contacts', {
    // This will get automatically plugged into the parent's state ui-view.
    // Since this is a top level state, its parent's ui-view is the root
    // unnamed view, aka 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
        "@" : { } 
  });

You can see how this ability to not only set multiple views within the same state but ancestor states could become a veritable playground for developer :).

View Loaded Event

Once the view is loaded (DOM is rendered), the '$scope' of the view emits an event - '$viewContentLoaded', that can be captured by the view's scope and any parent of it until the '$rootScope'. You can use this event to perform actions on the view once the view content has been loaded.

var viewController = function($scope){
    $scope.$on('$viewContentLoaded', function(){
        console.log('View Content Loaded');
    });
};

◄ Back (Nested States & Nested Views)     Next (URL Routing) ►

Clone this wiki locally