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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Nikas Praninskas
May 17, 2015
Technology
430
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
330
Move over, Gatsby - React Static in Practice
nikaspran
1
690
Web Components: The (near) Future of Web Development
nikaspran
2
4k
Other Decks in Technology
See All in Technology
Bucharest Tech Week 2026 - Reinventing testing practices in the AI era
edeandrea
PRO
1
170
AI-DLCを “そのまま導入しなかった”話 ~組織に合わせてアジャストした 私たちの実践共有~
hiroramos4
PRO
0
210
Agile and AI Redmine Japan 2026
hiranabe
3
280
Oracle AI Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
4
3k
ロボティクスの技術 / Robotics Technology
ks91
PRO
0
110
【Cyber-sec+】経営層を"動かす"ための考え方
hssh2_bin
0
200
【Snowflake Summit 2026 Recap!!】Snowflake Summit Deep Dive: Security & Governance
civitaspo
1
270
気づかぬうちにセキュリティ負債を生むAPIキー運用
sgwrmctk
0
180
Lightning近況報告
kozy4324
0
190
人材育成分科会.pdf
_awache
4
300
【NRUG vol.18】KubernetesにおけるNew Relicデータ取得量削減の考え方
nrug_member
0
170
自宅LLMの話
jacopen
1
650
Featured
See All Featured
Measuring & Analyzing Core Web Vitals
bluesmoon
9
870
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.9k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
340
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
160
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
590
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Done Done
chrislema
186
16k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
170
Unsuck your backbone
ammeep
672
58k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
600
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