-
Notifications
You must be signed in to change notification settings - Fork 3k
Home
Next (Nested States & Nested Views) ►
This In-Depth Guide will take you through all aspects of the UI-Router and its components and options. If you just need a quick reference guide visit the Quick Reference
The new $stateProvider works similar to Angular's v1 router, but it focuses purely on state.
- A state corresponds to a "place" in the application in terms of the overall UI and navigation.
- A state describes (via the controller / template / view properties) what the UI looks like and does at that place.
- States often have things in common, and the primary way of factoring out these commonalities in this model is via the state hierarchy, i.e. parent/child states aka nested states.
A state in its simplest form can be added like this (typically within module.config):
<!-- in index.html -->
<body ng-controller="MainCtrl">
<section ui-view></section>
</body>
// in app-states.js (or whatever you want to name it)
$stateProvider.state('contacts', {
template: '<h1>My Contacts</h1>'
})
When a state is activated, its templates are automatically inserted into the ui-view
of its parent state's template. If its a top-level state—which contacts is because it has no parent state–then its parent template is index.html.
Right now, the 'contacts' state won't ever be activated. So let's see how we can activate a state.
There are three ways to activate a state:
- Navigate to that
url
associated with the state. Learn More. - Use
$state.go()
. High-level convenience method. Coming Soon! - Use
$state.transitionTo()
. Low-level transition method. Learn More.
There are several methods for configuring a state's template.
As seen above, the simplest way to set your template is via the template
config property.
$stateProvider.state('contacts', {
template: '<h1>My Contacts</h1>'
})
Instead of writing the template inline you can load a partial. This is probably how you'll set templates most of the time.
$stateProvider.state('contacts', {
templateUrl: 'contacts.html'
})
templateUrl can also be a function that returns a url. It takes one preset parameter, stateParams, which is not injected.
$stateProvider.state('contacts', {
templateUrl: function (stateParams){
return '/partials/contacts.' + stateParams.filterBy + '.html';
}
})
Or you can use a template provider function which can be injected, has access to locals, and must return template HTML, like this:
$stateProvider.state('contacts', {
templateProvider: function ($timeout, $stateParams) {
return $timeout(function () { return '<h1>'+$stateParams.contactId+'</h1>' }, 100);
}
})
You can assign a controller to your template. Warning: The controller will not be instantiated if template is not defined.
You set your controller like this:
$stateProvider.state('contacts', {
template: '<h1>{{title}}</h1>',
controller: function($scope){
$scope.title = 'My Contacts';
}
})
Or if you already have a controller defined on the module, like this:
$stateProvider.state('contacts', {
template: ...,
controller: 'ContactsCtrl'
})
Controllers can use the $scope.on() method to listen for events fired by state transitions.
Controllers are instantiated on an as-needed basis, when their corresponding scopes are created, i.e. when the user manually navigates to a state via a URL, $stateProvider will load the correct template into the view, then bind the controller to the template's scope.
You can use resolve to provide your controller with content or data that is custom to the state. Resolve is an optional map of dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $routeChangeSuccess event is fired.
The resolve
property is a map object. The map object contains key/value pairs of:
- key – {string}: a name of a dependency to be injected into the controller.
- factory - {string|function}:
- If string, then it is an alias for a service.
- Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.
Examples:
$stateProvider.state('myState', {
resolve:{
// Example using function with simple return value
// Tip: Probably won't use this often
simpleObj: function(){
return {value: 'simple!'};
},
// Example using function with returned promise
// Tip: This is the typical use case of resolve
promiseObj: function(){
// $http returns a promise for the url data
return $http({method: 'GET', url: '/someUrl'});
},
// Another promise example. If you need to do some
// processing of the result, use .then, and your
// promise is chained in for free.
// Tip: This is the typical use case of resolve
promiseObj2: function(){
return $http({method: 'GET', url: '/someUrl'})
.then (function (data) {
return doSomeStuffFirst(data);
});
},
// Example using a service by name as string
// This would look for a 'translations' service
// within the module and return it
// Tip: The service could return a promise and
// it would work just like the example above
translations: "translations",
// Example showing injection of service into
// resolve function. Service then returns a
// promise.
russian: function(translations){
// pretend that getLang is a service method
// that uses $http to fetch some translations
translations.getLang("russian");
},
// Example showing returning of custom made promise
greeting: function($timeout){
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('Hello!');
}, 1000);
return deferred.promise;
}
},
// The controller waits for every one of the above items to be
// completely resolved before insantiation. For example, the
// controller will not instatiate until promiseObj's promise has
// been resolved. Then those objects are injected into the controller
// and available for use.
controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, russian, greeting){
$scope.simple = simpleObj.value;
// You can be sure that promiseObj is ready to use!
$scope.items = promiseObj.items;
$scope.items = promiseObj2.items;
$scope.title = translations.getLang("english").title;
$scope.title = russian.title;
$scope.greeting = greeting;
}
})
Learn more about how resolved dependencies are inherited down to child states.
You can attach custom data to the state object (we recommend using a data
property to avoid conflicts).
// Example shows an object-based state and a string-based state
var contacts = {
name: 'contacts',
templateUrl: 'contacts.html',
data: {
customData1: 5,
customData2: "blue"
}
}
$stateProvider
.state(contacts)
.state('contacts.list', {
templateUrl: 'contacts.list.html',
data: {
customData1: 44,
customData2: "red"
}
})
With the above example states you could access the data like this:
function Ctrl($state){
console.log($state.current.data.customData1) // outputs 5;
console.log($state.current.data.customData2) // outputs "blue";
}
Learn more about how custom data properties are inherited down to child states.
There are also optional 'onEnter' and 'onExit' callbacks that get called when a state becomes active and inactive respectively. The callbacks also have access to all the resolved dependencies.
$stateProvider.state("contacts", {
template: '<h1>{{title}}</h1>',
resolve: { title: 'My Contacts' },
controller: function($scope, title){
$scope.title = 'My Contacts';
},
onEnter: function(title){
if(title){ ... do something ... }
},
onExit: function(title){
if(title){ ... do something ... }
}
})
All these events are fired at the $rootScope
level.
- $stateChangeStart - fired when the transition begins.
$scope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){ ... })
Note: This is the only state change event that can be prevented.
$scope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
// transitionTo() promise will be rejected with
// a 'transition prevented' error
})
- $stateChangeSuccess - fired once the state transition is complete.
$scope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){ ... })
- $stateChangeError - fired when an error occurs during transition.
$scope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){ ... })