Imaginons que l’on a créé deux composants courses et cours. Inclure le composant cours dans courses est très simple, vous l’avez déjà fait : il suffit d’inclure des balises (selector) du composant cours dans le template HTML de courses.
<input [(ngModel)]="idMonComp" placeholder="saisir un id"/>
<button (click)="rediriger()">Rediriger vers</button>
<app-cours />
<app-cours />
<p>{{titre()}}</p>
<ul>
@for (module of UE; track module.nom) {
<li>élément {{$index}} : {{module.nom}} : {{module.nb_etuds}} étuds</li>
}
</ul>
Cela produira l’affichage suivant, dans lequel les composants cours sont dans le rectangle rouge :
Toutefois, dans le TypeScript ci-dessus, on voit que la boucle @for parcourt tous les modules (des cours, donc) et on aimerait bien qu’au lieu de les afficher dans une liste, on les affiche dans des instances du composant cours. Pour cela, on peut envisager de boucler sur les <app-cours /> :
<input [(ngModel)]="idMonComp" placeholder="saisir un id"/>
<button (click)="rediriger()">Rediriger vers</button>
@for (module of UE; track module.nom) {
<app-cours />
}
Le souci, c’est qu’il faudrait informer chaque instance de cours de son nom et de son nombre d’étudiants, bref transmettre des informations du parent (courses) vers son enfant (cours). Pour cela, la solution la plus simple consiste à utiliser des inputs.
Pour exploiter les inputs, on va commencer par dire que le CoursComponent, l’enfant, possède un ou plusieurs attributs, qui sont des inputs. Ceux-ci se comportent comme des signaux classiques à deux exceptions près :
les attributs créés via des input sont en lecture seule, on ne peut pas les modifier a posteriori ;
leur valeur a vocation à être affectée par le parent (ici le composant Courses).
import {Component, input} from '@angular/core';
import {Course} from '../services/mon-service.service';
@Component({
selector: 'app-cours',
imports: [],
templateUrl: './cours.component.html',
styleUrl: './cours.component.scss'
})
export class CoursComponent {
// on crée ici un attribut dont on indique que la valeur sera un nombre et
// sera optionnellement transmise par le parent. Ici, 0 = valeur par défaut
index = input(0); // équivalent à input<number>(0);
// si on veut obliger le parent à transmettre une valeur, on utilise
// input.required à la place de input
module = input.required<Course>();
}
Comme, dans le TypeScript du composant CoursComponent, on a un signal (input) pour l’index et un pour le module, on peut les utiliser pour afficher leur valeur dans le template HTML du CoursComponent :
<ul>
<li>élément {{index()}} : {{module().nom}} : {{module().nb_etuds}} étuds</li>
</ul>
La dernière étape du processus consiste à ce que le parent (ici Courses) transmette une valeur pour l’index et une pour le module. Cela se fait par property binding :
<input [(ngModel)]="idMonComp" placeholder="saisir un id"/>
<button (click)="rediriger()">Rediriger vers</button>
@for (mymodule of UE; track mymodule.nom) {
<app-cours [index]="$index" [module]="mymodule" />
}
Ci-dessus, [index] et [module] sont le moyen d’affecter des valeurs aux attributs index et module de CoursComponent. L’affichage obtenu avec ces codes devient celui ci-dessous, dans lequel chaque rectangle rouge correspond à une instance du composant cours :
Créez un composant topics qui aura pour vocation de contenir la liste des
topics d'un cours donné. Rajoutez sa route dans le fichier app.routes.ts
de telle sorte que, lorsque l’on cliquera sur un cours donné dans la page des cours,
on arrivera sur sa page de topics (ses topics à lui, pas ceux d’un autre cours).
Pour l’instant, on ne récupère pas du backend les topics en question, on veut
juste que les informations contenues dans la route permettent de le faire.
Dans la page des cours, rajoutez à chaque cours une ancre permettant d’accéder à sa page de topics (pour l’instant, on ne récupère toujours pas leur liste, on se contente juste d’accéder à la page qui affichera tous ces topics).
Le haut de la page des topics doit contenir ce que l’on appelle un breadcrumb, c’est-à-dire une aide à la navigation :
Tous les cours / nom_du_cours_sélectionné
Créez un nouveau composant breadcrumb. Ce composant sera utilisé à la fois pour
les topics et les posts, vous allez donc le rendre assez générique. Pour cela,
dans le fichier breadcrumb.component.ts
, rajoutez la déclaration de l’interface
suivante :
export interface BreadcrumbData {
nom: string;
route: string;
}
qui représente les données d’un item du breadcrumb (par exemple « Tous les cours » ou « nom_du_cours_sélectionné »). Le champ nom représente ce qui s’affichera sur votre navigateur et le champ route indique la route / le lien à suivre pour atteindre l’item correspondant. Par exemple la route correspondant au nom « Tous les cours » devrait être '/cours'. Les données nécessaires pour afficher tout un breadcrumb peuvent donc être stockées sous la forme d’un tableau de BreadcrumbData. Par exemple, le breadcrumb de la liste des topics du cours « Prog web et mobile » correspond au tableau :
[
{ nom: 'Tous les cours', route: '/cours' },
{ nom: 'Prog web et mobile', route: '' }
]
Ici, on supposera que, si une route est une chaîne vide, c’est que l’item correspondant doit être affiché sans lien HTML. Dans cet exercice, contentez-vous d’ajouter l’interface BreadcrumbData.
Faites en sorte que le BreadcrumbComponent puisse récupérer un tableau de BreadcrumbData transmis par son parent. Appelons paths l’attribut (le champ) de BreadcrumbComponent qui reçoit ce tableau. Dans la métode ngOnInit() de BreadcrumbComponent, affichez dans la console la valeur de l’attribut paths.
Pour cette séance, le parent du breadcrumb est la classe TopicsComponent. Rajoutez-lui un attribut breadcrumb de type BreadcrumbData[] et remplissez le dans sa méthode ngOnInit. Pour l’instant, on va simplifier et considérer que le nom du cours correspond à son id.
Enfin, dans le fichier topics-component.html
, rajoutez une balise pour
visualiser votre breadcrumb. Vérifiez que, dans la console, vous voyez bien
les informations transmises au breadcrumb.
Lorsque cela fonctionne, modifiez votre attribut breadcrumb de manière à ce qu’il ne soit pas de type BreadcrumbData[] mais que ce soit plutôt un signal sur ce type.
Dans le fichier breadcrumb.component.html
, écrivez le code permettant
l’affichage du breadcrumb. Ici, le plus simple est d’utiliser les
breadcrumbs de bootstrap, cf. l’url :
https://getbootstrap.com/docs/5.3/components/breadcrumb
On souhaite maintenant remplacer l’affichage de l’id du cours dans le breadcrumb des topics par le nom de ce cours (comme dans le forum de démonstration). Trouver une solution pour cela (réfléchissez avant de regarder la solution).
Dans le composant TopicsComponent, faites maintenant en sorte de récupérer dans son ngOnInit() la liste des topics du cours correspondant à sa route et affichez-les dans une <table> dans le template HTML (on ne s’intéresse pas à la beauté de l’application).