Jusqu’ici, pour afficher un composant, on plaçait sa balise dans le template HTML du composant racine app. Mais, évidemment, dans une vraie application, on ne peut pas faire cela car on veut pouvoir avoir plusieurs pages, pas juste une. Dans le jargon des weberies, on ne parle pas d’url pour ces pages, mais de route. On va donc voir maintenant comment créer des routes et on en créera une par page que l’on souhaite afficher (donc une pour se logguer sur le forum, une pour afficher la liste des cours, une pour les topics, etc.).
Les routes de l’application sont spécifiées dans le fichier app.routes.ts
. Pour
l’instant, il n’en contient aucune, ce qui veut dire qu’on ne peut accéder qu’à la
racine de votre application :
import { Routes } from '@angular/router';
export const routes: Routes = [];
La variable routes est un tableau dont chaque élément spécifiera une route. Remplissons le :
import { Routes } from '@angular/router';
import {CoursesComponent} from './courses/courses.component';
import {MonCompComponent} from './mon-comp/mon-comp.component';
// toutes les routes (url) de l'application.
export const routes: Routes = [
// l'url /courses contiendra une page affichant le composant Courses
{path: 'courses', component: CoursesComponent},
// si, dans le path, il y a des ":", cela indique que ce sont
// des paramètres, dont on pourra récupérer la valeur. Ainsi,
// si l'utilisateur accède à l'URL /mon-comp/42, le paramètre
// id aura pour valeur 42
{path: 'mon-comp/:id', component: MonCompComponent},
// on peut faire des redirections : ici, la combinaison des valeurs de
// path et pathMatch indiquent que la racine de l'application
// (http://127.0.0.1:4200/) doit être redirigée vers l'url
// http://127.0.0.1:4200/mon-comp/1234
{path: '', pathMatch: 'full', redirectTo: '/mon-comp/1234'},
// le wildcard ** signifie n'importe quelle url. Ici, ce sont toutes les
// urls qui ne correspondent pas à celles du dessus.
{path: '**', component: CoursesComponent}
];
Une route est donc un objet Javascript ayant une propriété path qui indique l’url à utiliser pour afficher le contenu de la route. Généralement, il y a également une propriété component qui indique quel composant il faut utiliser pour remplir la page.
Attention
Lorsque l’on demande à accéder à une page, une url, de votre application, Angular parcourt le tableau routes dans l’ordre où vous avez spécifiées ces routes. La première qui matche l’URL indique le composant à afficher. Il faut donc spécifier les routes de la plus spécifique à la moins spécifique. Par exemple, la route ** doit être en dernier dans le tableau.
Souvent la route ** est associée soit au composant à la racine de l’application (celui avec lequel on se loggue) soit avec un composant qui indique que l’url n’existe pas. Notez que l’on peut aussi proposer des redirections de pages. C’est ce que j’ai fait avec la 3ème route.
Si on utilise les routes, le fichier app.component.html
est relativement vide.
Dans celui ci-dessous, on voit une balise <h1></h1>, qui correspondra à une
sorte de navbar et une balise <router-outlet />. Souvent, vous n’aurez pas
un composant app plus complexe que cela.
<h1>Ceci est mon composant App</h1>
<router-outlet />
L’idée d’Angular c’est que le contenu de la balise <router-outlet /> est substituée par le composant correspondant à la route que l’on affiche. Par exemple, si le navigateur accède à l’url http://127.0.0.1:4200/courses, le contenu du fichier ci-dessous devient :
<h1>Ceci est mon composant App</h1>
<app-courses />
La page affichée correspond donc à celle ci-dessous. La partie rectangulaire en rouge correspond au composant Courses et, au dessus, on a bien la balise <h1></h1>.
Rajoutez une route spécifique pour la page d’authentification de votre forum.
Faites également en sorte que la page par défaut, celle de l’url
http://127.0.0.1:4200, soit aussi la page d’authentification. Maintenant, vous
n’avez plus besoin de votre balise <app-login /> dans le fichier
app.component.html
.
Si une page souhaite fournir un accès à une autre page via un bouton ou une
ancre, elle peut le faire via la propriété routerLink.
Rajoutons au fichier courses.component.html
les 3 premières lignes suivantes :
<nav>
<a routerLink="/mon-comp/42">Mon comp 42</a>
</nav>
<p>{{titre()}}</p>
<ul>
@for (module of UE; track module.nom) {
<li>élément {{$index}} : {{module.nom}} : {{module.nb_etuds}} étuds</li>
}
</ul>
@switch (nb_UE) {
@case(0) {
<li>Pas d'élément dans UE</li>
}
@case(1) {
<li>un seul élément dans UE</li>
}
@default {
<li>plusieurs éléments dans UE</li>
}
}
Alors, si le navigateur accède à l’url http://127.0.0.1:4200/courses, on obtient l’affichage suivant :
Si on clique sur l’ancre « Mon comp 42 », on est automatiquement redirigé vers l’url http://127.0.0.1:4200/mon-comp/42 comme le montre l’image ci-dessous. Notez que l’url associée à la propriété routerLink est spécifiée par rapport à la racine de l’application /.
Attention
Les urls indiquées ci-dessous sont virtuelles. Elles n’existent pas dans la réalité. Tous les navigateurs modernes supportent les urls virtuelles. Quand le navigateur demande à accéder à /mon-comp/42, le navigateur demande en réalité à Angular de recréer un DOM correspondant à cette route.
Attention
En Angular, il est important que les ancres utilisent routerLink plutôt que href. En effet, href aura pour effet de recharger entièrement votre application à partir du serveur alors que routerLink ne télécharge aucun fichier, il se contente de modifier le DOM.
Dans le code ci-dessus, on a affecté une route fixe, /mon-comp/42 au routerLink mais, dans certains cas, on souhaiterait calculer cette route dynamiquement en fonction des actions qu’a déjà réalisées l’utilisateur ou en fonction des données que l’on a reçues du backend. Dans ce cas, il suffit d’utiliser le property binding sur la propriété routerLink, comme le montre le code ci-dessous. Le plus simple est alors de scinder la route en morceaux en utilisant / comme séparateur et de placer ceux-ci dans un tableau. Dans le code ci-dessous, la route reviendrait à la chaîne de caractères « /mon-comp/{{nb_UE}} ».
<nav>
<a [routerLink]="['/mon-comp', nb_UE]">Mon comp nb UE</a>
</nav>
<p>{{titre()}}</p>
<ul>
@for (module of UE; track module.nom) {
<li>élément {{$index}} : {{module.nom}} : {{module.nb_etuds}} étuds</li>
}
</ul>
@switch (nb_UE) {
@case(0) {
<li>Pas d'élément dans UE</li>
}
@case(1) {
<li>un seul élément dans UE</li>
}
@default {
<li>plusieurs éléments dans UE</li>
}
}
Un composant peut bien évidemment récupérer les paramètres de la route actuelle. Pour cela, il faut que sa classe TypeScript contienne une instance de ActivatedRoute. Ensuite, il suffit d’appeler sa propriété snapshot.params, comme le montre le code ci-dessous. Dans ce code, comme la route spécifiée dans app.routes.ts est path: 'mon-comp/:id', le nom du paramètre s’appelle id.
import { Component } from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-mon-comp',
imports: [],
templateUrl: './mon-comp.component.html',
styleUrl: './mon-comp.component.scss'
})
export class MonCompComponent {
// la valeur du paramètre id de la route
myParam !: number;
// ActivatedRoute permet de récupérer des informations sur la route actuelle
constructor(private activatedRoute: ActivatedRoute) { }
ngOnInit(): void {
this.myParam = this.activatedRoute.snapshot.params['id'];
}
}
<p>mon-comp works!</p>
<p>paramètre de la route : {{myParam}}</p>
Ce code afficherait la page suivante :
On pourrait envisager des routes (et vous le ferez avec votre forum) qui contiennent plusieurs paramètres, par exemple :
{path: 'mon-comp/:idCours/:IdTopic', component: ...}
Dans ce cas, on peut récupérer ses paramètres grâce aux lignes suivantes dans le TypeScript du composant :
....
ngOnInit(): void {
this.myParamCours = this.activatedRoute.snapshot.params['idCours'];
this.myParamTopic = this.activatedRoute.snapshot.params['idTopic'];
}
....
Jusqu’ici, c’est le template HTML qui, via routerLink, permettait de passer d’une page à l’autre. Vous vous en servirez pour passer, par exemple, de la page des cours à celle des topics. Mais il peut également être intéressant que ce soit le TypeScript qui effectue cette démarche. Ce sera notamment le cas pour passer de la page de login à celle des cours. En effet, quand vous cliquez sur le bouton de connexion, le TypeScript interroge le backend pour déterminer si le login/password est valide. Le cas échéant, le TypeScript doit vous rediriger vers la page des cours.
Pour effectuer cela, il suffit de récupérer une instance de Router par dependency injection et d’utiliser sa méthode navigate ou navigateByUrl, qui vous redirigera vers la route passée en argument.
import {Component, OnInit, signal} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {Course, MonServiceService} from '../services/mon-service.service';
import {Router} from '@angular/router';
@Component({
selector: 'app-courses',
imports: [
FormsModule
],
templateUrl: './courses.component.html',
styleUrl: './courses.component.scss'
})
export class CoursesComponent implements OnInit {
titre = signal('composant courses');
UE : Course[] = [];
idMonComp !: string;
constructor(private service : MonServiceService,
private router : Router) {}
ngOnInit() {
this.service.getCourses().subscribe(result => {
this.UE = result;
});
}
rediriger() {
if (typeof this.idMonComp === 'undefined') this.idMonComp = 'vide';
this.router.navigateByUrl(`/mon-comp/${this.idMonComp}`);
}
}
<input [(ngModel)]="idMonComp" placeholder="saisir un id"/>
<button (click)="rediriger()">Rediriger vers</button>
<p>{{titre()}}</p>
<ul>
@for (module of UE; track module.nom) {
<li>élément {{$index}} : {{module.nom}} : {{module.nb_etuds}} étuds</li>
}
</ul>
Cela affichera la page ci-dessous. Quand on saisit une chaîne de caractères dans l’input, le two-way binding met à jour la valeur de l’attribut idMonComp. Lorsque l’on clique sur le bouton, par event binding, la méthode rediriger() est exécutée et this.router.navigateByUrl redirige vers la route demandée. Par exemple, si idMonComp = "toto", on est redirigé vers la route /mon-comp/toto.
Créez un nouveau composant cours qui, plus tard, correspondra à la page des cours. Créez une route pour ce composant.
Si, dans la page de login, le message de retour de checkLogin.php
indique que
l’authentification est réussie, faites en sorte de vous rediriger sur cette page
de cours.
Dans la méthode ngOnInit du composant CoursComponent, faites en sorte de récupérer du backend la liste des cours suivis par l’utilisateur, et mettez à jour votre backend afin que celui-ci vous envoie ces informations (n’oubliez pas de vous servir de la variable $_SESSION de PHP).
Affichez dans une console la liste des informations transmises par votre backend afin de débugger.
Dans le fichier cours.component.ts
, créez une interface Cours
indiquant le type des données contenues dans les enregistrements retournés par
votre backend (autrement dit celles contenues dans les éléments du champ
data du PhpData).
Dans le TypeScript du CoursComponent, stockez la liste des cours que vous avez récupérée du backend dans un tableau de Cours et affichez la à l’intérieur d’une table HTML dans le template HTML. Pour l’instant, on ne s’intéresse pas au visuel de cette table. Ce n’est pas grave si ce n’est pas joli.