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
4k
Other Decks in Technology
See All in Technology
Zoom2Youtube.Claude
kawaguti
PRO
3
490
Road to SRE NEXTの今までとこれから
hiroyaonoe
0
310
KiCAD講習会②
tutcreators
0
110
ゴールデンパスは敷いただけでは道にならない ─ 企画部門のエンジニアが技術標準を事業価値に変えるまで
mhrtech
0
140
Claude Code公式skillで 自分の仕事を少しずつ手放そう!(Claude Code開発ノウハウ大公開スペシャル by クラスメソッド)
kaym
1
270
Making sense of Google’s agentic dev tools
glaforge
1
180
AI駆動開発におけるQAエンジニアの役割事例 〜AI駆動開発の現場から〜
kobayashiyorimitsu
0
490
実装だけじゃない! CCA-F取得エンジニアが教えるClaude Code開発プロセス活用術
diggymo
2
640
Gen3R: 3D Scene Generation Meets Feed-Forward Reconstruction
spatial_ai_network
0
120
AIと共生する開発者プラットフォーム:バクラクのモノレポ×マイクロサービス基盤
sakajunquality
2
3.4k
DatabricksにおけるMCPソリューション
taka_aki
1
240
インフラ寄りSREでも 開発に踏み出せる〜境界を越えてユーザー体験に向き合いたい〜
sansantech
PRO
2
3.7k
Featured
See All Featured
Tell your own story through comics
letsgokoyo
1
990
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.1k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
72
40k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
650
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Context Engineering - Making Every Token Count
addyosmani
9
1k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
55k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
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