◄ Back (State Manager)
Next (Multiple Named Views) ►
States can be nested within each other. There are several ways of nesting states:
.state('contacts.list', {})
.parent
property with the parent name as string. For example: parent: 'contacts'
parent
property with the parent object. For example parent: contacts
(where 'contacts' is a stateObject)You can use dot syntax to infer your hierarchy to the $stateProvider. Below, contacts.list
becomes a child of contacts
.
$stateProvider .state('contacts', {}) .state('contacts.list', {});
This is a 3rd party module created by @marklagendijk. So you have to include it in addition to ui-router. Visit the stateHelper repo to learn more
angular.module('myApp', ['ui.router', 'ui.router.stateHelper']) .config(function(stateHelperProvider){ stateHelperProvider.state({ name: 'root', templateUrl: 'root.html', children: [ { name: 'contacts', templateUrl: 'contacts.html', children: [ { name: 'list', templateUrl: 'contacts.list.html' } ] }, { name: 'products', templateUrl: 'products.html', children: [ { name: 'list', templateUrl: 'products.list.html' } ] } ] }); });Parent Property using State Name String
Alternately, you can specify the parent of a state via the parent
property.
$stateProvider .state('contacts', {}) .state('list', { parent: 'contacts' });
If you aren't fond of using string-based states, you can also use object-based states. The name
property goes in the object and the parent
property must be set on all child states, like this:
var contacts = { name: 'contacts', templateUrl: 'contacts.html' } var contactsList = { name: 'list', parent: contacts, templateUrl: 'contacts.list.html' } $stateProvider .state(contacts) .state(contactsList)
You can usually reference the object directly when using other methods and property comparisons:
$state.transitionTo(states.contacts); $state.current === states.contacts; $state.includes(states.contacts)
You can register states in any order and across modules. You can register children before the parent state exists. It will queue them up and once the parent state is registered then the child will be registered.
If you register only a single state, like contacts.list
, you MUST define a state called contacts
at some point, or else no states will be registered. The state contacts.list
will get queued until contacts
is defined. You will not see any errors if you do this, so be careful that you define the parent in order for the child to get properly registered.
No two states can have the same name. When using dot notation the parent
is inferred, but this doesn't change the name of the state. When explicitly providing a parent using the parent
property, state names still must be unique. For example, you can't have two different states named "edit" even if they have different parents.
When the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well. Below, when the "contacts.list" state is active, the "contacts" state is implicitly active as well, because it's the parent state to "contacts.list".
Child states will load their templates into their parent's ui-view
.
Full Plunkr Here: http://plnkr.co/edit/7FD5Wf?p=preview
$stateProvider .state('contacts', { templateUrl: 'contacts.html', controller: function($scope){ $scope.contacts = [{ name: 'Alice' }, { name: 'Bob' }]; } }) .state('contacts.list', { templateUrl: 'contacts.list.html' }); function MainCtrl($state){ $state.transitionTo('contacts.list'); }
<!-- index.html --> <body ng-controller="MainCtrl"> <div ui-view></div> </body>
<!-- contacts.html --> <h1>My Contacts</h1> <div ui-view></div>
<!-- contacts.list.html --> <ul> <li ng-repeat="contact in contacts"> <a>{{contact.name}}</a> </li> </ul>What Do Child States Inherit From Parent States?
Child states DO inherit the following from parent states:
Nothing else is inherited (no controllers, templates, url, etc). However, children of abstract states do inherit the url
property of their parent as a prefix of their own url
.
New in version 0.2.0
Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states.
$stateProvider.state('parent', { resolve:{ resA: function(){ return {'value': 'A'}; } }, controller: function($scope, resA){ $scope.resA = resA.value; } }) .state('parent.child', { resolve:{ resB: function(resA){ return {'value': resA.value + 'B'}; } }, controller: function($scope, resA, resB){ $scope.resA2 = resA.value; $scope.resB = resB.value; }
NOTE:
resolve
keyword MUST be relative to state
not views
(in case you use multiple views).Child states will inherit data properties from parent state(s), which they can overwrite.
$stateProvider.state('parent', { data:{ customData1: "Hello", customData2: "World!" } }) .state('parent.child', { data:{ // customData1 inherited from 'parent' // but we'll overwrite customData2 customData2: "UI-Router!" } }); $rootScope.$on('$stateChangeStart', function(event, toState){ var greeting = toState.data.customData1 + " " + toState.data.customData2; console.log(greeting); // Would print "Hello World!" when 'parent' is activated // Would print "Hello UI-Router!" when 'parent.child' is activated })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.
View Inherited Resolved DependenciesViews may inherit resolved dependencies from the state that they belong to, but may not inherit those of their sibling views.
$stateProvider.state('myState', { resolve:{ resMyState: function(){ return { value: 'mystate' }; } }, views: { 'foo@myState': { templateUrl: 'mystate-foo.html', controller: function($scope, resMyState, resFoo){ /* has access to resMyState and resFoo, but *not* resBar */ }, resolve: { resFoo: function() { return { value: 'foo' }; } }, }, 'bar@myState': { templateUrl: 'mystate-bar.html', controller: function($scope, resMyState, resBar){ /* has access to resMyState and resBar, but *not* resFoo */ }, resolve: { resBar: function() { return { value: 'bar' }; }, }, }, }, });
An abstract state can have child states but can not get activated itself. An 'abstract' state is simply a state that can't be transitioned to. It is activated implicitly when one of its descendants are activated.
Some examples of how you might use an abstract state are:
url
to all child state urls.template
with its own ui-view(s)
that its child states will populate.
controller
to the template. The controller must pair to a template.resolve
for use by child states.data
for use by child states or an event listener.onEnter
or onExit
function that may modify the application in someway.Remember: Abstract states still need their own <ui-view/>
for their children to plug into. So if you are using an abstract state just to prepend a url, set resolves/data, or run an onEnter/Exit function, then you'll additionally need to set template: "<ui-view/>"
.
To prepend url to child state urls
$stateProvider .state('contacts', { abstract: true, url: '/contacts', // Note: abstract still needs a ui-view for its children to populate. // You can simply add it inline here. template: '<ui-view/>' }) .state('contacts.list', { // url will become '/contacts/list' url: '/list' //...more }) .state('contacts.detail', { // url will become '/contacts/detail' url: '/detail', //...more })
To insert a template with its own ui-view
for child states to populate
$stateProvider .state('contacts', { abstract: true, templateUrl: 'contacts.html' }) .state('contacts.list', { // loaded into ui-view of parent's template templateUrl: 'contacts.list.html' }) .state('contacts.detail', { // loaded into ui-view of parent's template templateUrl: 'contacts.detail.html' })
<!-- contacts.html --> <h1>Contacts Page</h1> <div ui-view></div>
Combination
Shows prepended url, inserted template, paired controller, and inherited $scope object.
Full Plunkr Here: http://plnkr.co/edit/gmtcE2?p=preview
$stateProvider .state('contacts', { abstract: true, url: '/contacts', templateUrl: 'contacts.html', controller: function($scope){ $scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }]; } }) .state('contacts.list', { url: '/list', templateUrl: 'contacts.list.html' }) .state('contacts.detail', { url: '/:id', templateUrl: 'contacts.detail.html', controller: function($scope, $stateParams){ $scope.person = $scope.contacts[$stateParams.id]; } })
<!-- contacts.html --> <h1>Contacts Page</h1> <div ui-view></div>
<!-- contacts.list.html --> <ul> <li ng-repeat="person in contacts"> <a ng-href="#/contacts/{{person.id}}">{{person.name}}</a> </li> </ul>
<!-- contacts.detail.html --> <h2>{{ person.name }}</h2>
◄ Back (State Manager)
Next (Multiple Named Views) ►
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4