(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Routing
app/app.module.ts
|
import { RouterModule } from '@angular/router';
// la première correspondance est utilisée
const routes = [
{ path: 'welcome', component: WelcomeComponent },
{ path: '', redirectTo: 'welcome', pathMatch: 'full' }, // les redirections ne s'enchainent pas, une seule est possible
{ path: 'items', component: ItemsComponent },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
useHash: true,
enableTracing: true // afficher dans la console les evts de routing
}),
ItemModule // ce module contient ses propres règles de routage
]
})
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';
// la première correspondance est utilisée
let routes = [
{ path: "welcome", component: WelcomeComponent },
{ path: "", redirectTo: "welcome", pathMatch: "full" }, // les redirections ne s'enchainent pas, une seule est possible
{ path: "items", component: ItemsComponent },
{ path: "**", component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(routes,
{
useHash: true,
enableTracing: false // to debug routing
}),
ItemModule // ce module contient ses propres règles de routage
],
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 {
// on utilise OnInit pour éviter de mettre du code asychrone dans le ctor
ngOnInit(): void {
// obtenir la valeur d'un paramètre
let id = +this.route.snapshot.paramMap.get('id');
// + : cast string → int
|
|
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 {
// on utilise OnInit pour éviter de mettre du code asychrone dans le ctor
ngOnInit(): void {
// exécuté à chaque modification des paramètres
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"> <!-- conserve les query params déjà définit -->
Back
</a>
|
|
this.router.navigate(['/items'],
{
queryParams: { filterBy: 'text', showImage: true }
}
);
// conserve les query params déjà définit
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
|
// component-less route: pas de component définit pour le path parent
const routes = [
{ path: 'items/:id',
children: [
{ path: '', redirectTo: 'info' },
{ path: 'info', component: ItemComponent },
{ path: 'edit', component: ItemEditComponent },
]
}
];
// naviguer vers info
this.router.navigate(['info'], { relativeTo: this.route });
|
item.component.html
|
<!-- menu -->
<div class="wizard">
<a [routerLink]="['info']">Info</a>
<a [routerLink]="['edit']">Edit</a>
</div>
<!-- définit l'espace d'affichage pour le contenu des child component -->
<router-outlet></router-outlet>
|
Style
|
<a [routerLink]="['/']" routerLinkActive="active">Items</a>
<!-- dans une liste il faut ajouter routerLinkActive à li -->
<ul>
<!-- [routerLinkActiveOptions]='{ exact: true }'
ne colore le lien que s'il y a correspondance exacte.
Par défaut, colore tous les liens qui correspondent -->
<li routerLinkActive="active" [routerLinkActiveOptions]='{ exact: true }'>
<a routerLink="/" (click)='collapse()'>
<span class='glyphicon glyphicon-list'></span> Items
<!-- afficher une icône d'erreur à côté du lien -->
<a [routerLink]="['/']" routerLinkActive="active">
Items <span [ngClass]="{'glyphicon glyphicon-list': hasError()}"></span>
</a>
|
Animation CSS de navigation
ClientApp/src/styles.css
|
/* Slide transition */
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);
}
}
/* Fade transition */
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
|
<!-- à la première ligne -->
<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
|
<!-- à la première ligne -->
<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']; // items → resolve: { items: ItemResolver }
}
|
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.