Happy New Year
Pre-releaseAfter a great deal of thought, hard work, and a lot of feedback and support from the community, the AngularUI Router team is proud to announce the first alpha release of UI Router 1.0. It is the culmination of a complete rewrite of the internal architecture of the 0.2.x series (as well as a port to TypeScript), which began over 18 months ago, and has resulted in a more robust, more flexible design, which addresses many of our community’s biggest requests and most pressing concerns.
Backwards Compatibility
Despite the massive refactor, we've managed to retain almost 100% API compatibility with 0.2.x, barring a few small semantic differences:
- Return values for
onEnter
/onExit
hooks are now meaningful (see below), so if you're using CoffeeScript or defining hooks with ES6 arrow functions, add&& null
to your return expression if you don't want to opt in to any new behavior - To ease debugging for new users, certain errors that would otherwise be swallowed (i.e. in resolves) are now printed to the console by default; this can be disabled with
$transitionsProvider.defaultErrorHandler(() => {})
(see more about the new$transitions
service below) - Due to the design of the new view system, templates are required for all views
- All state events, (i.e.
$stateChange*
and friends) are deprecated and disabled by default; re-enable them by including theng1/stateEvents.js
file, and depend on theui.router.state.events
angular module in your application.
New Features
The refactor has also enabled a number of important new features:
Transition hooks
http://angular-ui.github.io/ui-router/feature-1.0/interfaces/transition.ihookregistry.html
Internally, the state transition system is now implemented as a highly-configurable pipeline of operations and dependent relationships. This pipeline exposes a number of hook points that provide much more flexibility over the previous event-driven architecture.
The hooks system interface allows application code to be much more targeted around specific groups or types of transitions. Hooks have also been made more expressive by allowing meaningful return values. For example, this hook disables all transitions from children of foo
states to children of bar
states:
$transitionsProvider.onStart({ from: 'foo.*', to: 'bar.*' }, () => false);
Matching rules allow specific states to be targeted, and can target based on glob rules, as with $state.includes()
. Matching rules can also be functions, enabling patterns like the following:
$transitionsProvider.onStart({
to: (state) => !!state.data.requiresAuth
}, (UserService) => UserService.current().then(() => true, () => false));
Some things to note:
- The matching rule returns the boolean value of
requiresAuth
in the state's custom data to determine whether to enforce a current user session - The paired hook function is injectable, meaning it can accept any custom services, as well as some special injectable values (see below)
- This hook function returns a promise, which will hold the transition until the promise resolves, meaning you can use this pattern for implementing flows such as prompting for auth information, then resuming the transition
- Finally, the promise returns a boolean value; returning
false
from a hook (either through a promise or directly) will cancel the transition
The Transition
pipeline has first-class support for redirecting transitions. Simply return a TargetState
from a hook using the $state.target()
factory method.
Consult the 1.0 hook documentation for the full list of hooks and where they take place within the transition pipeline.
State-specific hooks
In addition to transition-level hooks (which are equivalent in scope to $stateChange*
events), $transitionsProvider
also implements state-specific hooks, which are essentially global versions of the standard onEnter
/onExit
hooks. These fire once per state being entered or exited, and provide a $state$
injectable, representing the state object being entered or exited. These hooks allow you to implement common enter/exit logic across groups of states.
We've also introduced a new hook, onRetain
, which allows you to define how a state should behave when it is held over during a transition.
The Transition
Object
http://angular-ui.github.io/ui-router/feature-1.0/classes/transition.transition-1.html
Instead of an endless list of parameters as in the state events of 0.2.x, all transition details are now fully encapsulated in the Transition
object. You can inspect parameters, origin and destination states, resolves, and trace back transition redirections.
The Transition object allows you to add or override resolves at runtime using the addResolves()
method, the Transition object exposes the same set of hooks as $transitionsProvider
, such that hooks (e.g., onFinish()
) can be applied on a per-transition basis.
The Transition
object is available as an injectable in any hook as $transition$
.
Resolve Policies
We've made it possible to tune exactly how resolves are handled, with the following options available:
EAGER
: All eager resolves for a transition are resolved at the beginning, before any states are entered (this is way UI-Router 0.2.x handles resolves)LAZY
: Lazy resolves are resolved when the state they are declared on is entered.JIT
: Just-In-Time resolves do not resolve until just before they are injected into some other function. (extremely lazy)
In the future, this will enable powerful progressive rendering capabilities.
Sample App, State Visualizer, Transition Visualizer
We're working on a new sample app (Source), as well as a state and transition visualizer. Check them out, and give us some feedback. We'd like to make the visualizers into a Chrome Extension that you can enable on any UI-Router 1.0 app.
Why UI Router?
With the advent of Angular 2 and the Component Router, we’re often asked if UI Router still has a place in the Angular ecosystem. From what we’ve observed, we believe that the answer to that question is an unequivocal yes. While Component Router offers a number of advancements in DOM handling and lifecycle management, it still couples routes 1:1 with components.
We believe that for sophisticated applications (and even not-so-sophisticated ones), a hierarchical state machine is the best abstraction boundary, allowing application code to stay declarative, and well organized. This, among many other reasons (like typed and dynamic parameters), is why we've been hearing over and over again from enterprise users to solo developers, that UI Router support is at or near the top of their needs list for migrating their applications to Angular 2.
The Future
Our immediate goal is to collect feedback on this release so that we can make sure we're not missing any important use cases or breaking compatibility in any unintended way. To do that, we need your help. Please test UI Router against your app, and let us know if you experience any issues. Try out the new features and let us know if there's anything (not already on the roadmap) that we're still missing.
After we've shipped the final 1.0 release, we're heading back into the workshop with the following priorities:
- Angular 2 Support: Unsurprisingly, this has been our most oft-request feature of late, and by converting the codebase to TypeScript, we're already a long way towards supporting it; our goal is to support Angular 1 and 2 simultaneously, and provide people who wish to stay with UI Router a smooth (as possible) transition path
- Pipeline API: We plan to further decouple the internal transition pipeline at the core of UI Router, and implement a public API for managing it — this will enable even more sophisticated UI state handling, such as parallel transitions and multiple active states (see: 'sticky states' from UI Router Extras)
- Lazy Loading & Runtime State Tree Management: Load, add, move, and remove states and state hierarchies on the fly
- Progressive Rendering: An important mobile-first feature, it will allow much finer tuning of the user experience while waiting for dependency data to resolve
- Documentation: We've switched to TypeDoc, for documentation generation. We still have a lot of work to do to get the documentation quality back up to our standards; bear with us.
Resources
- ngPittsburgh, Sept 2015: A talk that explains UI Router's philosophy, differences from Component Router, and new features in 1.0
Acknowledgements
- First and foremost, I'd like to thank my co-conspirator, Chris Thielen, especially for being longsuffering with my perfectionism
- Wesley Cho, for kick-starting the port to TypeScript, crushing some pretty difficult bugs, and generally being a great source of intel and encouragement
- Rob Wormald, for architectural inspiration, patience for our never-ending streams of questions, and general cleverness