UI-Router
http://slides.com/ansmith/ui-router-intro/live
Aaron Smith @aaron_n_smith
Dave Ackerman @dmackerman
Angular Routing without UI-Router
(ngRoute)
ngRoute, the default routing solution, comprises:
- $routeProvider
- $route
- $routeParams
- ngView
- works with $location
ngRoute Configuration
angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'views/home.html',
controller: 'HomeController'
})
.when('/detail', {
templateUrl: 'views/detail.html',
controller: 'DetailController'
})
.otherwise('/home');
});
ngRoute Parameters
angular.module('myApp', ['ngRoute'])
.controller('DetailCtrl', function($scope, $routeParams) {
$scope.record = $http.get('api/' + $routeParams.recordId);
});
.config(function($routeProvider) {
$routeProvider.when('/detail/:recordId', {
templateUrl: 'views/detail.html',
controller: 'DetailCtrl'
});
});
ngRoute Resolves
angular.module('myApp', ['ngRoute'])
.controller('DetailCtrl', function($scope, record) {
$scope.record = record;
})
.config(function($routeProvider) {
$routeProvider.when('/detail/:recordId', {
templateUrl: 'views/detail.html',
controller: 'DetailCtrl',
resolve: {
record: function($route, $http) {
var recId = $route.current.params.recordId;
return $http.get('api/' + recId);
}
}
});
});
ngView directive
<!DOCTYPE html>
<html ng-app>
<head></head>
<body>
<header>Some static header perhaps</header>
<div ng-view>
<!-- your route here -->
</div>
<footer>Obligatory privacy policy and copyright.</footer>
</body>
</html>
This sounds great!
Well, not so fast...
ngRoute is limiting
-
views: there can be only one ngView
-
resolves: must be independent
UI-Router addresses ngRoute's deficiencies...
and much, much more.
UI-Router: a little history
-
Part of the AngularUI umbrella project
-
Karsten Sperling, Tim Kindberg, Jens Melgaard
-
Started in early 2013
- 0.0.1 released in May 2013
- 0.2.10 (latest stable) release
UI-Router @ 10,000ft
- Multiple, concurrent views
- State-driven
- Nested views
- Nested resolves
- onEnter/onExit events (+ others)
ngRoute vs. UI-Router
$routeProvider
$route
$routeParams
ng-view
href
→
→
→
→
→
$urlRouterProvider
$state
$stateParams
ui-view
ui-sref
*
Some "Advanced" Concepts
- Inheriting resolved dependencies to child states.
- Splitting UI elements into their own components
- Preventing access to a particular state (authenticated states)
- View loading events
- Named views
Inherited Resolve Dependencies
$stateProvider.state('feed', { url: '/feed' resolve: { 'feedData': function() { return 'foo'; } } }) .state('feed.detail', { url: '/feed/:itemId' resolve: { 'singleFeedData': function(feedData, $state) {
// can access the current state with
$state.current... // $state object. return feedData; // the inherited resolve. } } });
Breaking Up Your $stateProvider
- Long $stateProviders are hard to read!
- Break your apps states into modules which share common functionality (your code should be doing this anyway!)
angular.module('main', ['main.contacts', 'ui.router']) .config(function($stateProvider) { $stateProvider.state('main', {
...
}) }); angular.module('main.contacts', ['ui.router']) .config(function($stateProvider) { $stateProvider.state('main.contacts', { ... }) });
Events
$scope.$on('$stateChangeError', function(event, toState, ... , error) {
// catch errors from resolves in here! });
- One of the most useful events for detecting errors in resolved dependencies.
- Also useful for things such as loading masks / indicators.
View Events
Happen when a state becomes active and inactive.
$stateProvider.state("contacts", { templateUrl: 'contacts.tpl.html', resolve: { data: function() { return 'My Contacts'; },
onEnter: function(data){ if(data){ ... do something ... } }, onExit: function(title){ if(data){ ... do something ... } } })
Stopping a state transition
(with Authentication)
$stateProvider.state('admin', {
url: '/admin',
data: {
pageTitle: 'Admin Console',
requiresAuth: true
}
...
})
$scope.$on('$stateChangeStart', function(event, toState, toParams... )
// check authentication in your "auth service".
if (toState.data.requiresAuth && !AuthService.isAuthenticated()) {
$state.transitionTo('login');
event.preventDefault(); // stop state transition
}
});
Named Views
<header ui-view="header"></header>
<nav ui-view="switcher"></nav>
<div ui-view="menu" autoscroll="false"></div>
- Powerful concept, but use with caution.
- Not every section in your app needs to be named.
Named Views (cont)
$stateProvider.state('home', {
url: '/home',
views: {
'main': {
templateUrl: 'home/home.tpl.html'
}
}
})
.state('home.confirm', {
url: '/confirm',
views: {
// target and override the named view "main" in the parent state
'main@': {
templateUrl: 'home/confirm.tpl.html'
}
}
});
- viewName@stateName for targetting.
Don't overcomplicate.
Abstract States
- Useful if you need to prepend a URL to your child states.
- Also useful for prepopulating child resolves with data.
// cannot activate this state directly.
.state('parent', { url: '/home', abstract: true })
// ALSO url '/home', overriding its parent's activation
.state('parent.list', { url: '', abstract: true })
Tips & Tricks
- Putting $state and $stateParams on your $rootScope/$scope can be useful for state dependent changes on your UI.
- Always (read: ALWAYS) include a $stateChangeError listener in your app.
- Use autoscroll="false" on your ui-views to avoid saving scroll position.
- States don't need URLs. Useful for things like modals, asides - things that are global but not tied directly to states.
- Be careful with state naming, and be aware of targeting. Don't overnest states. Avoid things like: app.home.contacts.details.add
Animations!
because everyone loves animations.
- Really easy with the new version of ngAnimate.
-
Check out: http://mgcrea.github.io/angular-motion/
- Make sure your content areas have a height specified or things will be janky.
ui-sref-active
-
By default, doesn't work for nested states.
-
Workaround!!
<a ng-class="{ active: $state.includes('app.dashboard')}" ui-sref="app.dashboard">Dashboard</a>
- Only works if you put $state on your $scope.
Other useful stuff.
- Modals tied to a state? Use onEnter and onExit to open/close them.
- Animation is relatively painless with newer versions of ngAnimate
- $state.reload() - resolves are re-resolved, events are not re-fired, and controllers are re-instantiated
- You can use regex's in URL matching for states.
-
url: '/{userId:[0-9]{1,4}}',
Demo App
The FUTURE
The Angular team is very aware the current
routing solution is sub-par.
And they're going to fix it!
.... in 2.0
Thanks!
Questions?
Aaron Smith @aaron_n_smith
Dave Ackerman @dmackerman
UI-Router Intro
By Aaron N. Smith
UI-Router Intro
Dive into AngularJS routing with UI-Router. Overview and comparison of ngRoute and UI-Router as well as tips, tricks and usage recommendations for UI-Router.
- 3,683