Getting the most out of Angular Router
@filip.mam
@filip.mam
filip.mam
@filip.mam
@filip.mam
+
+
=
SPAs
@filip.mam
Getting the most out of Angular Router
@filip.mam
Agenda
-
Why Router?
-
Meet the app
-
Level up the app!
ROUTING
LAZY LOADING
RESOLVING
@filip.mam
Why router?
@filip.mam
making SPAs with no Router
@filip.mam
making SPAs with Router
@filip.mam
Meet the app
@filip.mam
<app-nav></app-nav>
<app-movies-ranking></app-movies-ranking>
<app-tv-shows-ranking></app-tv-shows-ranking>
<app-movie-finder></app-movie-finder>
app.html
app-nav.html
<cw-button>Movies ranking</cw-buton>
<cw-button>TV shows ranking</cw-buton>
<cw-button>Movie finder</cw-buton>
@filip.mam
<app-nav></app-nav>
<app-movies-ranking *ngIf="activeIndex === 0"></app-movie-base>
<app-tv-shows-ranking *ngIf="activeIndex === 1"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
app.html
app-nav.html
<cw-button (click)="activeIndex = 0">Movies ranking</cw-buton>
<cw-button (click)="activeIndex = 1">TV shows ranking</cw-buton>
<cw-button (click)="activeIndex = 2">Movie finder</cw-buton>
Chapter 1
The ugly duck
@filip.mam
@filip.mam
<app-nav></app-nav>
<app-welcome-text *ngIf="activeIndex === undefined"></app-welcome-text>
<app-movies-ranking *ngIf="activeIndex === 0"></app-movies-ranking>
<app-tv-shows-ranking *ngIf="activeIndex === 1"></app-tv-shows-ranking>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
app.html
@filip.mam
<app-nav></app-nav>
<app-welcome-text *ngIf="activeIndex === undefined"></app-welcome-text>
<app-movies-ranking *ngIf="activeIndex === 0"></app-movies-ranking>
<app-tv-shows-ranking *ngIf="activeIndex === 1"></app-tv-shows-ranking>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 3"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 4"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 5"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 6"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 7"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 8"></app-box-office>
app.html
@filip.mam
<app-nav></app-nav>
<app-welcome-text *ngIf="activeIndex === undefined"></app-welcome-text>
<app-movies-ranking *ngIf="activeIndex === 0"></app-movies-ranking>
<app-tv-shows-ranking *ngIf="activeIndex === 1"></app-tv-shows-ranking>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 3"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 4"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 5"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 6"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 7"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 8"></app-box-office>
<app-welcome-text *ngIf="activeIndex === 9"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 10"></app-movie-base>
<app-actors-base *ngIf="activeIndex === 11"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 12"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 33"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 14"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 15"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 16"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 17"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 18"></app-box-office>
<app-welcome-text *ngIf="activeIndex === 19"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 20"></app-movie-base>
app.html
@filip.mam
<app-nav></app-nav>
<app-welcome-text *ngIf="activeIndex === undefined"></app-welcome-text>
<app-movies-ranking *ngIf="activeIndex === 0"></app-movies-ranking>
<app-tv-shows-ranking *ngIf="activeIndex === 1"></app-tv-shows-ranking>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 3"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 4"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 5"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 6"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 7"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 8"></app-box-office>
<app-welcome-text *ngIf="activeIndex === 9"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 10"></app-movie-base>
<app-actors-base *ngIf="activeIndex === 11"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 12"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 33"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 14"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 15"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 16"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 17"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 18"></app-box-office>
<app-welcome-text *ngIf="activeIndex === 19"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 20"></app-movie-base>
<app-actors-base *ngIf="activeIndex === 21"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 22"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 23"></app-box-office>
<app-goodbay-text *ngIf="activeIndex === 24"></app-welcome-text>
<app-actors-base *ngIf="activeIndex === 25"></app-movie-base>
<app-tag-base *ngIf="activeIndex === 26"></app-actors-base>
<app-tag-finder *ngIf="activeIndex === 27"></app-movie-finder>
<app-show-office *ngIf="activeIndex === 28"></app-box-office>
<app-welcome-text *ngIf="activeIndex === 29"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 30"></app-movie-base>
<app-actors-base *ngIf="activeIndex === 31"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 32"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 33"></app-box-office>
app.html
@filip.mam
@filip.mam
Chapter 2
The Route Knight
@filip.mam
@filip.mam
What? When? Where?
@filip.mam
const routes = [{
path: "movie-finder",
component: MovieFinderComponent
}]
const routes = [{
path: "movie-finder",
component: MovieFinderComponent
}]
@NgModule(
declarations: [MovieFinderComponent],
imports: [RouterModule.forRoot(routes)]
)
export class AppModule {}
app.module.ts
Routing configuration - When? What?
@filip.mam
<header>Need something to watch?</header>
<app-nav></app-nav>
<app-welcome-text></app-welcome-text>
<app-movie-base></app-movie-base>
<app-actors-base></app-actors-base>
<app-movie-finder></app-movie-finder>
<app-box-office></app-box-office>
app.html
<header>Need something to watch?</header>
<app-nav></app-nav>
<header>Need something to watch?</header>
<app-nav></app-nav>
<router-outlet></router-outlet>
Routing configuration - Where?
@filip.mam
const routes = [{
path: "movie-finder",
component: MovieFinderComponent
}]
app.module.ts
const routes = [{
path: "movie-finder",
component: MovieFinderComponent
},
{
path: "tv-shows-base",
component: ActorsBaseComponent
},
{
path: "movies-base",
component: MoviesBaseComponent
}]
@filip.mam
app-nav.html
<cw-button routerLink="movies-base">Movies base</cw-buton>
<cw-button routerLink="tv-shows-base">TV shows base</cw-buton>
<cw-button routerLink="movie-finder">Movie finder</cw-buton>
<cw-button (click)="goToMovieBase()">Movies base</cw-buton>
...
goToMovieBase(): void {
this.routerService.navigate('/movie-base');
}
<cw-button (click)="activeIndex = 0">Movies ranking</cw-buton>
<cw-button (click)="activeIndex = 1">TV shows ranking</cw-buton>
<cw-button (click)="activeIndex = 2">Movie finder</cw-buton>
@filip.mam
app.html
<header>Need something to watch?</header>
<app-nav></app-nav>
<app-welcome-text *ngIf="activeIndex === undefined"></app-welcome-text>
<app-movie-base *ngIf="activeIndex === 0"></app-movie-base>
<app-actors-base *ngIf="activeIndex === 1"></app-actors-base>
<app-movie-finder *ngIf="activeIndex === 2"></app-movie-finder>
<app-box-office *ngIf="activeIndex === 3"></app-box-office>
<header>Need something to watch?</header>
<app-nav></app-nav>
<router-outlet></router-outlet>
@filip.mam
Improvments
1. Fix app architecture
@filip.mam
main.js
movies
ranking
tvShows
ranking
movie
finder
commons
vendors
main.pack.js
@filip.mam
movie
ranking
tvShows
ranking
movie
finder
commons
vendors
main.pack.js
@filip.mam
@filip.mam
Chapter 3
Lazy load all the things
@filip.mam
commons
vendors
main.pack.js
@filip.mam
shows ranking
commons
vendors
main.pack.js
1.pack.js
@filip.mam
shows
ranking
commons
vendors
main.pack.js
1.pack.js
movie
finder
.js
@filip.mam
Lazy load components
modules
@filip.mam
@Component({
...
})
export class MovieFinderComponent {
...
}
movie-finder.component.ts
@NgModule(
declarations: [MovieFinderComponent]
)
export class MovieFinderModule {}
movie-finder.module.ts
@NgModule(
declarations: [MovieFinderComponent],
imports: [RouterModule.forChildren(movieFinderRoutes)]
)
export class MovieFinderModule {}
const movieFinderRoutes = [{
path: "",
component: MovieFinderComponent
}]
@filip.mam
app.module.ts
const routes = [{
path: "movie-finder",
component: MovieFinderComponent
},
{
path: "tv-shows-base",
component: ActorsBaseComponent
},
{
path: "movies-base",
component: MoviesBaseComponent
}]
const routes = [{
path: "movie-finder",
loadChildren:
},
{
path: "tv-shows-base",
loadChildren:
},
{
path: "movies-base",
loadChildren:
}]
const routes = [{
path: "movie-finder",
loadChildren: "./modules/movie-finder.module/#MovieFinderModule"
},
{
path: "tv-shows-base",
loadChildren: "./modules/tv-shows-base.module/#TvShowsBaseModule"
},
{
path: "movies-base",
loadChildren: "./modules/movies-base.module/#MoviesBaseModule"
}]
@filip.mam
Improvments
1. Fix app architecture
2. Reduce the size of bootstrapped app
@filip.mam
MovieRankingComponet
movieList = [...]
...
<div *ngFor="let movie of movieList">
{{movie.title}} {{movie.year}}
</div>
@filip.mam
@Component({
...
})
class MoviesRankingComponent {
movieList: Movie[] = [
{title: "Godfather", year: "1972"},
{title: "Inception", year: "2010"},
...
]
}
MovieRankingComponet
with static data
@filip.mam
MovieRankingComponet
with async data
@Component({
...
})
class MoviesRankingComponent {
movieList: Movie[] = [];
constructor(http: Http) {
http.get('api/movies').subscribe(
movies => this.movieList = movies;
);
}
}
@filip.mam
component created with empty fields
asynchronus data request
data comes back, empty fields are filled
1
2
3
MovieRankingComponet
lifecycle with async data
@filip.mam
2
3
async request time
@filip.mam
2
3
async request time
@filip.mam
Chapter 4
(re)solving problems
@filip.mam
/movies-ranking
/movie-finder
/tv-shows-ranking
@filip.mam
/movie-finder
I need some data m8
@filip.mam
const moviesRankingRoutes = [{
path: "",
component: moviesRankingComponent,
resolver: {
movies: MoviesResolver
}
}]
movie-ranking.module.ts
@filip.mam
@Injectable()
class MoviesResolver implement Resolve<Movie[]> {
constructor(http: Http) {}
public resolve(): Observable<Movie[]> {
return this.http.get("api/movies");
}
}
movies-resolver.service.ts
@filip.mam
route recognized
resolver fired, http get request
data resolved and ready to be served
1
2
3
component is constructed
4
MovieRankingComponet
with resolver
@filip.mam
resolver
@Component({
...
})
class MovieFinderComponent {
movieList: Movie[] = [];
constructor(route: ActivatedRouteSnapshot) {
this.movieList = route.data.movies
}
}
movie-finder.component.ts
@filip.mam
Improvments
1. Fix app architecture
2. Reduce the size of bootstrapped app
3. Prevent loading fails
@filip.mam
Chapter 5
Composing resolvers
@filip.mam
movie-ranking.module.ts
public resolve(): Observable<Movie[]> {
return this.http.get("api/tv-shows");
}
...
const tvShowsRankingRoutes = [{
path: "",
component: moviesRankingComponent,
resolver: {
tvShows: TvShowsResolver
}
}]
...
constructor(route: ActivatedRouteSnapshot) {
this.tvShows = route.data.tvShows
}
@filip.mam
movie-ranking.module.ts
const movieFinderRoutes = [{
path: "",
component: moviesRankingComponent,
resolver: {
movies: MoviesResolver,
tvShows: TvShowsResolver
}
}]
...
constructor(route: ActivatedRouteSnapshot) {
this.movies = route.data.movies;
this.tvShows = route.data.tvShows;
}
@filip.mam
Improvments
1. Fix app architecture
2. Reduce the size of bootstrapped app
3. Prevent loading fails
4. Create reusable resolvers
@filip.mam
@filip.mam
@filip.mam
Chapter 6
Router events
@filip.mam
@Component({
template: "<div>LOADING...</div>",
styleUrls: ["./stlyes.url"]
})
class LoaderComponent {
...
}
loader.component.ts
@filip.mam
<header>Need something to watch?</header>
<app-nav></app-nav>
<router-outlet></router-outlet>
<router-outlet name="loader"></router-outlet>
app.html
@filip.mam
routes = [
...,
{
path: "loader",
component: LoaderComponent,
outlet: "loader"
}
]
app.module.ts
@filip.mam
constructor(router: Router) {}
...
this.router.events.subscribe(event) {
if (event instanceof ResolveStart) {
this.showLoader();
}
if (event (instanceof ResolveEnd) {
this.hideLoader();
}
}
app.ts
@filip.mam
@filip.mam
Improvments
1. Fix app architecture
2. Reduce the size of bootstrapped app
3. Prevent loading fails
4. Create reusable resolvers
5. Added loading mechanism
@filip.mam
Chapter 7
Thanks!
Get the most out of Angular Router - Angular Kraków #2
By Filip Mamcarczyk
Get the most out of Angular Router - Angular Kraków #2
- 870