Routing
app/app.module.ts
|
import { RouterModule } from '@angular/router';
const routes = [
{ path: 'welcome', component: WelcomeComponent },
{ path: '', redirectTo: 'welcome', pathMatch: 'full' },
{ path: 'items', component: ItemsComponent },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
useHash: true,
enableTracing: true
}),
ItemModule
]
})
export class AppModule { }
|
app/app.component.html
|
<router-outlet></router-outlet>
|
|
<a routerLink="/path"></a>
|
|
import { Router } from '@angular/router';
export class MyComponent {
contructor(private router: Router) { }
onClick() {
this.router.navigate("/path");
}
|
app-routing.module.ts
Permet d'isoler le routing dans un fichier à part.
app/app.module.ts
|
import { AppRoutingModule } from './app-routing.module';
@NgModule({
imports: [
AppRoutingModule
],
})
export class AppModule { }
|
app/app-routing.module.ts
|
import { RouterModule } from '@angular/router';
let routes = [
{ path: "welcome", component: WelcomeComponent },
{ path: "", redirectTo: "welcome", pathMatch: "full" },
{ path: "items", component: ItemsComponent },
{ path: "**", component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(routes,
{
useHash: true,
enableTracing: false
}),
ItemModule
],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
|
Child Route
ClientApp/src/app/module1/module1.module.ts
|
import { RouterModule } from '@angular/router';
let routes = [
{ path: "component1", component: Component1Component }
];
@NgModule({
imports: [
RouterModule.forChild(routes)
]
})
export class Module1Module { }
|
Hash-based urls
Par défaut les urls sont de style HTML 5: .../welcome
- nécessite une réécriture des urls côté serveur
Pour utiliser les urls de style hash-based: .../#/welcome il faut ajouter useHash: true dans les options du RouterModule
Noms des routes
Item List |
items
|
Item Detail |
items/:id
|
Item Edit |
items/:id/edit
|
Paramètres de routage
app/app.module.ts
|
{ path: "items/:id", component: ItemDetailComponent }
|
|
<a [routerLink]="['/items', item.id]">{{ item.name }}</a>
|
|
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) { }
|
Snapshot
|
import { OnInit } from '@angular/core';
export class ItemDetailComponent implements OnInit {
ngOnInit(): void {
let id = +this.route.snapshot.paramMap.get('id');
|
 |
Récupération des paramètres à l'initialisation du composant, si les paramètres sont modifiés mais pas l'url alors le composant n'est pas réinitialisé et on ne récupère pas les nouvelles valeurs des paramètres. |
Observable
|
import { OnInit } from '@angular/core';
export class ItemDetailComponent implements OnInit {
ngOnInit(): void {
this.route.paramMap.subscribe(
params => {
let id = +params.get('id');
}
);
|
Paramètres optionnels
|
<a [routerLink]="['/items', { name: itemName, id: itemId }]"></a>
|
Query parameters
|
<a [routerLink]="['/items']"
[queryParams]="{ filterBy: 'text', showImage: true }">
</a>
<a [routerLink]="['/items']"
[preserveQueryParams]="true">
Back
</a>
|
|
this.router.navigate(['/items'],
{
queryParams: { filterBy: 'text', showImage: true }
}
);
this.router.navigate(['/items'],
{ preserveQueryParams: true }
);
|
Child routes
Permet de définir des règles de routages dans les modules et ainsi définir une hiérarchie dans les routes.
component-less routes module: module n'activant pas de composants, contenant juste des child routes.
Il définit son propre <router-outlet> qui contiendra le contenu des onglets par exemple.
Les child routes permettent une navigation master/detail ou avec des onglets.
child routes sont nécessaires pour le lazy loading.
item.component.ts
|
const routes = [
{ path: 'items/:id',
children: [
{ path: '', redirectTo: 'info' },
{ path: 'info', component: ItemComponent },
{ path: 'edit', component: ItemEditComponent },
]
}
];
this.router.navigate(['info'], { relativeTo: this.route });
|
item.component.html
|
<div class="wizard">
<a [routerLink]="['info']">Info</a>
<a [routerLink]="['edit']">Edit</a>
</div>
<router-outlet></router-outlet>
|
Style
|
<a [routerLink]="['/']" routerLinkActive="active">Items</a>
<ul>
<li routerLinkActive="active" [routerLinkActiveOptions]='{ exact: true }'>
<a routerLink="/" (click)='collapse()'>
<span class='glyphicon glyphicon-list'></span> Items
<a [routerLink]="['/']" routerLinkActive="active">
Items <span [ngClass]="{'glyphicon glyphicon-list': hasError()}"></span>
</a>
|
Animation CSS de navigation
ClientApp/src/styles.css
|
app-root div.panel.panel-primary {
animation-duration: .5s;
animation-name: slideIn;
animation-fill-mode: forwards;
transition: transform .5s ease;
}
@keyframes slideIn {
from {
transform: translate(-200px);
}
to {
transform: translate(0px);
}
}
app-root div {
animation-duration: .5s;
opacity: 0;
animation-name: fadeIn;
animation-fill-mode: forwards;
transition: transform .5s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
|
Spinner pour le temps d'attente lors du chargement avant navigation
Fonctionne avec un resolver sinon la page s'affiche directement.
app/app.component.ts
|
import { Router, Event, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router';
loading = true;
constructor(private router: Router) {
router.events.subscribe((routerEvent: Event) => this.checkRouterEvent(routerEvent));
}
checkRouterEvent(routerEvent: Event): void {
if (routerEvent instanceof NavigationStart) {
this.loading = true;
} else if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationError ||
routerEvent instanceof NavigationCancel) {
this.loading = false;
}
}
|
app/app.component.html
|
<span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="loading"></span>
|
style.css
|
.spinner {
font-size: 300%;
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
}
.glyphicon-spin {
-webkit-animation: spin 1000ms infinite linear;
animation: spin 1000ms infinite linear;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
|
Spinner pour le temps d'attente lors du chargement avant navigation
Fonctionne avec un resolver sinon la page s'affiche directement.
app/app.component.ts
|
import { Router, Event, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router';
loading = true;
constructor(private router: Router) {
router.events.subscribe((routerEvent: Event) => this.checkRouterEvent(routerEvent));
}
checkRouterEvent(routerEvent: Event): void {
if (routerEvent instanceof NavigationStart) {
this.loading = true;
} else if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationError ||
routerEvent instanceof NavigationCancel) {
this.loading = false;
}
}
|
app/app.component.html
|
<span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="loading"></span>
|
style.css
|
.spinner {
font-size: 300%;
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
}
.glyphicon-spin {
-webkit-animation: spin 1000ms infinite linear;
animation: spin 1000ms infinite linear;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
|
Secondary Routes
Dans le cas où un sous-composent a son propre menu, il faut donc gérer les routes du menu principal et les routes du menu du sous-composant.
Route Guards
- contrôler les droits d'accès aux composants
- empêcher la sortie d'un composant (ex: s'il manque des données à un formulaire)
Route Guards
- contrôler les droits d'accès aux composants (ex: administrateur ou utilisateur loggué)
- empêcher la sortie d'un composant (ex: s'il manque des données à un formulaire)
- récupérer les données avant d'afficher la page (resolver)
Récupérer les données avec un Route Resolver
Sans Route Resolver, le template est affiché sans donnée, puis une requête récupère les données et les affiche.
Avec Route Resolver, les données sont récupérées puis le template et les données sont affichés en même temps.
app.module.ts
|
import { ItemResolver } from 'ClientApp/app/components/items/items-resolver.service';
let routes = [
{ path: "items", component: ItemsComponent, resolve: { items: ItemResolver } },
];
@NgModule({
providers: [
ItemResolver
],
|
items-resolver.service.ts
|
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class ItemResolver implements Resolve<Item[]> {
constructor(private dataService: DataService) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Item[] | Observable<Item[]> | Promise<Item[]> {
return this.dataService.loadItems();
}
|
items.component.ts
|
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {
this.items = this.route.snapshot.data['items'];
}
|
Lazy loading modules
- définir sur un module, ainsi tous les composants de ce module seront chargés de manière asynchrone
- configurer dans le routing dans le parent
- ne pas importer le module asynchrone dans un autre module
app.module.ts
|
const routes = [
{ path: 'home', component: HomeComponent },
{ path: 'images', loadChildren: 'app/images/image.module#ImageModule' }
];
|
CanLoad Guard: ne pas télécharger le module si l'utilisateur n'y a pas accès.