Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
angular/router
Search
Nikas Praninskas
May 17, 2015
Technology
440
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
angular/router
The New Angular Router
Nikas Praninskas
May 17, 2015
More Decks by Nikas Praninskas
See All by Nikas Praninskas
Static Site Generators with JavaScript
nikaspran
1
340
Move over, Gatsby - React Static in Practice
nikaspran
1
700
Web Components: The (near) Future of Web Development
nikaspran
2
4.1k
Other Decks in Technology
See All in Technology
【GCC2026】TrueHDRIを用いたルックデブ環境とライティングテクニック
bandainamcostudios
PRO
0
130
Adding Right-to-Left support to your web application with CSS logical properties — Lessons from Redmine
vividtone
0
160
AIペネトレーションテスト・ セキュリティ検証「AgenticSec」紹介資料
laysakura
2
9.3k
私がブラウザを自作したくなった理由
supurazako
1
270
AI for Science時代を切り開く、政府の次世代HPC戦略の展望
gpuunite_official
0
120
LLM・AIエージェントシステムベストプラクティス
shibuiwilliam
6
1.6k
AI画像認識を活用したゲーム内決済処理検証の自動化
gree_tech
PRO
0
100
小粒でもパワフルなJS Runtime Antjsについて
comamoca
0
110
【書籍出版記念】 10周回って、エージェント開発は RAGがすべてだった。〜RAGの歴史と開発現場で見えた実践知〜
akiratameto
3
410
Digitization部 紹介資料
sansan33
PRO
2
7.7k
Invisible to AI? Making TYPO3 Sites Quotable by AI Search Systems
wolfgangwagner
0
240
ガバメント AI 源内を地方自治体は活用できるのか可能性と課題、期待について
takeda_h
2
460
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
780
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.1k
How to Ace a Technical Interview
jacobian
281
24k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
[SF Ruby Conf 2025] Rails X
palkan
2
1.3k
We Are The Robots
honzajavorek
0
300
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
410
Crafting Experiences
bethany
1
260
Transcript
angular/router The New Angular Router
Hi, I’m Nikas Front end dev @ Wix.com/app-market @nikaspran github/nikaspran
nikas.praninskas.com
The next 45 minutes of your life Prior art What
is the New Angular Router? Migration Current state of affairs
Disclaimer: everything is Work In Progress
Prior art
What ARE frogs Routers?
Deep linking is awesome share information know at a glance
contextualize bookmark The ability to
Don’t break the back button The back button is free
and obvious It is very easy to be annoying if you break navigation
Structure for your app Forces you to modularize Makes apps
easy to understand
ngRoute The default Angular 1 routing solution
ngRoute - very simple .config(function($routeProvider){ $routeProvider .when('/user/:id', { templateUrl: 'user.html',
controller: 'UserCtrl', resolve: {...} }) .when('/user/:id/edit', { templateUrl: 'userEdit.html', controller: 'UserEditCtrl' }); }); One view per app One URL, one view <a href="user/123"></a> <a href="user/123/edit"></a> <div ng-view></div>
Good things come to those who wait .when('/user/:id', { ...,
resolve: { user: function($q) { var data = $q.defer(); //get user data from API return data.promise; } } }) Inject into controller from configuration Waits until promises resolve
Simple, but too simple Difficult to do things like modals
sidebars deep routes parallel views complex apps
UI Router The de facto Angular 1 routing solution
UI Router - much more powerful <a ui-sref="layout.user"></a> <a ui-sref="layout.user.edit(
{id: 123})"></a> <div ui-view="sidebar"></div> <div ui-view="content"></div> State based routing .config(function($stateProvider){ $stateProvider .state('layout', { abstract: true, templateUrl: 'layout.html' }) .state('layout.user', { views: { sidebar: { templateUrl: 'sidebar.html', controller: 'SidebarCtrl', resolve: {...} }, content: {...} } }) });
States are intuitive States describe architecture Not everything has to
have a URL Easy to link to parts of your application
Lots of utility <a ui-sref="user.edit({id: 123})"></a> <a href="/base/user/123"></a>
<ui-sref-active> $state.is() $state.go() $state.href() $state.includes() Lots of utility …
Customizability is very important and UI Router does a pretty
good job: christopherthielen/ui-router-extras onEnter, onExit, $stateChange*, ...
…up to a point Do something complex enough and you
start fighting the router
Something complex enough
Same URL, different background state
Multiple paths that preserve history 1. Open App Market 2.
Click App icon Use direct link to App (eg. via social) wix.com/app-market/:appName/overview
we spent way too much time fighting our tools at
the time, we did a fork still not obvious how to do it all
Angular 2 is coming Can we do better with a
clean slate?
Introducing The New Angular Router
Introducing The New Angular Router
Introducing Component Router
Goals
Be customizable… Let people do complex interesting surprising amazing things
…but have great defaults Make it obvious intuitive quick easy
to learn and use
Multiple views /foo/bar foo bar foo bar parallel nested OR
State-like reverse routing Automatically deep link to components <a router-link="user"
router-params="{id: 123}"></a> <a href="./user/123"></a>
State-like reverse routing Link to multiple components at once sidebar
content mainPage <a router-link="mainPage"></a>
Thought experiment: The router is one of the most important
parts of an application
So… If it is designed for Angular 2
If it is designed for Angular 2 but also works
with Angular 1
If it is designed for Angular 2 but also works
with Angular 1 We have a migration plan! ng1 ng2
Proposed migration scenario ng1 ng1 ng1 ng1 ng1 ng1 Angular
1 app
Proposed migration scenario ng1 ng1 ng1 ng2 ng1 ng1
Proposed migration scenario ng1 ng2 ng1 ng2 ng2 ng1
Proposed migration scenario ng2 ng2 ng2 ng2 ng2 ng2 Angular
2 app
ま よ た ね ぃ み む ょ じ を
り ぉ ね ぼ り ぉ け ば や わ ゎ お ら ゆ ろ り ぉ け ば や わ ゎ お ら ゆ じ ゃ で り ぐ き な ゕ ゟ た ょ を こ で ぷ お じ ぁ だ ど ぬ す っ ら び ぅ ぬ だ ざ ま よ た ね ぃ み む ょ じ を り ぉ ね ぼ び ぃ ぅ や こ そ ぶ や あ ゝ ぼ ゆ ぺ み ひ じ え わ ぶ す ぞ ぷ ゑ っ ぞ ほ だ Show me the code already ま よ た ね ぃ み む ょ じ を り ぉ ね ぼ ろ り ぉ け ば や わ ゎ お ら ゆ ほ ぞ ぁ ろ り ぉ け ば や わ ゎ お び ぃ ぅ や こ そ ぶ や あ ゝ ぼ ゆ ぺ み ひ じ え わ ぶ す ぞ ぷ ゑ っ ぞ ほ だ ま よ た ね ぃ み む ょ じ を ろ り ぉ け ば や わ ゎ お ら ゆ ほ び ぃ ぅ や こ そ ぶ や あ ゝ ぼ ゆ ぺ み ひ じ え わ ぶ す ぞ ぷ ゑ っ ま よ た ね ぃ み む ょ じ を り ぉ ね ぼ ろ り ぉ け ば や わ ゎ お ら ゆ ほ ぞ ぁ ろ り ぉ け ば や わ ゎ び ぃ ぅ や こ そ ぶ や あ ゝ ぼ ゆ ぺ み ぶ す ぞ ぷ ゑ っ ぞ ほ だ り ぉ け ば や わ ゎ お ら ゆ ほ ぞ ぁ ろ り ぉ け ば や わ ゎ お ぉ け ば や わ ゎ お ら ゆ ほ ろ り ぉ け ば や わ ゎ お ら ゆ け ば や わ ゎ お ら ゆ ほ ぞ ぁ ろ り ぉ け ば や わ ゎ お ら ゆ を こ で ぷ お じ ぁ だ ど う え を こ で ぷ お じ ぁ だ ど う え ぬ す っ ら び ぅ ぬ だ ざ ぬ す っ ら び ぅ ぬ だ ざ ぬ す っ ら び ぅ ぬ だ ざ ぶ す ぞ ぷ ゑ っ ぞ ほ だ ゝ ぼ ゆ ぺ み あ ゝ ぼ ゆ ぺ み あ ゝ ぼ ゆ ぺ み あ ゝ ぼ ゆ ぺ み ぬ す っ ら び ぅ ぬ す っ ら び ぅ ぬ だ ざ ぬ す っ ら び ぅ ぬ だ ざ ぬ す っ ら び ぅ ぬ だ ざ ぬ す っ ら び ぅ ぬ あ ゝ ぼ ゆ ぺ み ゝ ぼ ゆ ぺ み ま よ た ね び ぃ ぅ や こ そ ぶ ま よ た ね ぃ み
Angular 2.x import {ListComponent} from './list'; import {EditComponent} from './edit';
@Component({...}) @View({template: '...'}) @RouteConfig([ {path: '/', component: ListComponent}, {path: '/edit/:id', component: EditComponent} ]) class AppComponent() { ... }
Angular 1.x $componentMapper .setCtrlNameMapping(comp => comp + 'Ctrl'); $componentMapper .setTemplateMapping(comp
=> comp + '.html'); AppController.$routeConfig([ {path: '/', component: 'list'}, {path: '/edit/:id', component: 'edit'} ]); function AppController() { ... }
Static configuration {path: '/', component: 'list'}, {path: '/edit/:id', component: 'edit'}
{path: '/', component: ListComponent}, {path: '/edit/:id', component: EditComponent} ng2 ng1 === easy to port
Component Router Components Outlets Instructions Pipeline
Component Router Components Outlets Instructions Pipeline
So what’s a component anyway? Template Controller Router
Template - ng2 @View({template: '...'}) @View({templateUrl: '...'})
Template - ng1 $componentMapper .setTemplateMapping(function (compName) { return templatePath; });
./components/my-comp/my-comp.html myComp Uses component convention by default: Customize as you want:
“Controller” - ng2 class MyComp() {...}
Controller - ng1 $componentMapper .setCtrlNameMapping(function (compName) { return controllerName; });
Adds “Controller” by default: Customize as you want: MyCompController myComp
Router Each component can have a child router Child routers
manage their sub-URLs
You can release it as an artifact They can mount
and use it SO,if you build an amazing component with deep linking
Router - ng2 @RouteConfig([ {path: '/', redirectTo: '/main'}, {path: '/main',
component: MainComp}, {path: '/other', components: {}, as: 'other'} ])
Router - ng1 function MainController($router) { $router.config([...]); } function MainController()
{...}; MainController.$routeConfig([...]);
Component Router Components Outlets Instructions Pipeline
Outlet - think ui-view <div ng-outlet></div> <div ng-outlet="left"></div> {path: '/',
components: { default: 'posts', left: 'users' }}
Component Router Components Outlets Instructions Pipeline
Instruction Holds all the context for a transition: Outlets, path,
parameters, ...
Component Router Components Outlets Instructions Pipeline
Pipeline - it’s middleware Takes Instruction Transforms it ..with side
effects $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep
E.g.: pipeline in Angular 1.x $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep
$activateStep Pipeline
Let’s ask the router to go somewhere $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep
$runCanActivateHookStep $loadTemplatesStep $activateStep router.navigate('users/123')
Router generates the instruction $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep
router.navigate('users/123') recognize(): instruction matchedUrl component router children: Instruction[]
Once more unto the pipeline! $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep
$activateStep matchedUrl component router children: Instruction[]
$setupRoutersStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] Add child instruction routers
$initLocalsStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] locals Add locals: $router, $routeParams
$runCanDeactivateHookStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] locals Visit old outlets; run canDeactivate()
$runCanActivateHookStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] locals Visit new outlets; run canActivate()
$loadTemplatesStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] locals template Load templates, add to instruction
$activateStep $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep matchedUrl component router
children: Instruction[] locals template Recursively activate outlets (ctrl, tmpl, ...)
Final step… Update $location: http://someHost/base/users/123 … and done!
Pipeline - it’s customizable $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep
Up to you
E.g.: let’s add resolve $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep
resolveStep
Create our step factory .factory('resolveStep', function ($q, $injector) { return
function resolve(instruction) { }; })
Recurse over instructions .factory('resolveStep', function ($q, $injector) { return function
resolve(instruction) { var promises = []; return instruction.router.traverseInstruction( instruction, queueResolves ).then(function () { return $q.all(promises); }); }; })
Get config from instruction .factory('resolveStep', function ($q, $injector) { return
function resolve(instruction) { var promises = []; function queueResolves(instruction) { var cfg = getResolveConfigFrom(instruction); } return instruction.router.traverseInstruction( instruction, queueResolves ).then(function () { return $q.all(promises); }); }; })
Queue the resolution .factory('resolveStep', function ($q, $injector) { return function
resolve(instruction) { var promises = []; function queueResolves(instruction) { var cfg = getResolveConfigFrom(instruction); var resolve = $q.when($injector.invoke(cfg.value)); promises.push(resolve.then(function (resolved) { })); } return instruction.router.traverseInstruction( instruction, queueResolves ).then(function () { return $q.all(promises); }); }; })
Add it to locals .factory('resolveStep', function ($q, $injector) { return
function resolve(instruction) { var promises = []; function queueResolves(instruction) { var cfg = getResolveConfigFrom(instruction); var resolve = $q.when($injector.invoke(cfg.value)); promises.push(resolve.then(function (resolved) { instruction.locals[cfg.key] = resolved; })); } return instruction.router.traverseInstruction( instruction, queueResolves ).then(function () { return $q.all(promises); }); }; })
Insert it into the pipeline .config(function ($pipelineProvider) { var steps
= $pipelineProvider.steps; var atPosition = steps.indexOf('$runCanDeactivateHookStep) + 1; //insert resolveStep after $runCanDeactivateHookStep steps.splice(atPosition, 0, 'resolveStep'); $pipelineProvider.config(steps); })
End result $setupRoutersStep $initLocalsStep $runCanDeactivateHookStep $runCanActivateHookStep $loadTemplatesStep $activateStep resolveStep Part
of the pipeline Called on every transition
> implications Standardized extension Make it suit your app Publish
and prosper
Component Router Components Outlets Instructions Pipeline Other “stuff”
Sticky components This is how it currently works Under consideration
and desirable
Dynamic loading Add new routes @ runtime: function SomeController($router) {
$router.config([...]); }
Recursive components Not sure how practical, but why not
Migration
ng1 + ngComponentRouter ng2 ng 1.5 + new router Some
things we know
And some we don’t ng1 + X ng1 + ngComponentRouter
ng2 ng 1.5 + new router ?
ngRoute is too simple to worry about
Migration strategies from UI Router are a TODO item But
one can dream…
Multi-components are sort of like states $router.config([{ as: 'category', path:
'/:category', components: { sidebar: 'Sidebar', content: 'Category' } }]); $stateProvider .state('category', { url: '/:category', views: { sidebar: { templateUrl: '...', controller: '...' }, content: { templateUrl: '...', controller: '...' } } }); ui-router compRouter
Directives are sort of similar <a router-link="user" router-params="{id: 123}"></a> <a
ui-sref="user({id: 123})"></a> ~== ui-router compRouter
What about what’s missing? Well…
Pipeline! e.g. we saw how to add resolve we can
fake $stateChangeEvents someone write an adapter? :)
How can we prepare? Use similar conventions Component-like architecture Component-like
file structure
Write unit tests UI Router configuration is testable Write now,
then verify migration later http://nikas.praninskas.com/test-ui-router Component Router should be even easier
Current $state of affairs //TODO: draw something totally awesome like
robots and lasers and stuff and maybe a cat
Documentation!
Read the source, Luke: github.com/angular/router
github.com/angular/angular …no wait, now it’s:
but only temporarily…
Codebase is a bit of a mess: 1.x has not
worked for over a month everything in ng2 repo until build is stable still working out core issues (i.e. reverse routing)
When? Originally slated for 1.4
…but probably not anymore: :(
June-July? (my somewhat educated guess)
In place of a summary Designed from the ground up;
New ideas for the future; Lessons learned from the past; Not yet ready.
Thank you! nikas.praninskas.com github/nikaspran @nikaspran