Exercice 1 : Forum - Installations   

Pour l’instant, au mieux, vous avez utilisé bootstrap pour que votre application ait un visual « pas trop moche ». On va maintenant s’intéresser à Angular Material qui, à peu de frais, va lui donner un look un peu moderne. Pour cela, installer via la commande npm install les packages suivants :

  1. @angular/material et @angular/cdk : Angular Material contient un ensemble de composants prêts à être utilisés, qui simplifieront votre code, cf. l’URL :

    https://material.angular.io/components/categories

  2. @ckeditor/ckeditor5-angular et @ckeditor/ckeditor5-build-classic, qui vous permettront d’avoir à disposition dans Angular un éditeur agréable pour saisir vos posts, cf. l’URL :

    https://ckeditor.com/ckeditor-5/

Dans le fichier angular.json, là où vous aviez ajouté les fichiers de style de bootstrap, ajoutez ceux d’Angular Material. Il y a plusieurs thèmes disponibles. Celui que j’ai utilisé dans le forum de démonstration et l’indigo-pink, qui correspond à l’ajout suivant dans angular.json :

angular.json
"styles": [
  "src/styles.scss",
  "node_modules/bootstrap/scss/bootstrap.scss",
  "node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
],

Exercice 2 : Forum - Des tables stylées   

Angular Material propose un composant pour afficher des tables, cf. l’URL :

https://material.angular.io/components/table/overview

Lisez le premier exemple de l’onglet « OVERVIEW » de cette page. La section « Getting Started » débute par un exemple et vous noterez qu’en cliquant en haut à droite de celui-ci sur l’icône <>, vous pourrez visualiser le template HTML et le code TypeScript de l’exemple. Essayez de comprendre la partie HTML et la partie TypeScript. Quand vous pensez que c’est le cas, dévoilez l’indice 1, qui résume ce qu’il faut retenir.

Indice 1 

  • Dans le template HTML :

    • "dataSource" fait référence à l’attribut de CoursComponent contenant les données de la base de données;

    • Toutes les lignes à l’intérieur de la table à l’exception des deux <tr> à la fin décrivent les colonnes de table (leur header ainsi que la manière de remplir les valeurs des cellules de cette colonne).

    • les matColumnDef correspondent aux noms des colonnes de votre base de données;

    • dans les balises <th>, on indique les titres des colonnes;

    • dans les element.champ des <td>, les champs correspondent aux noms des colonnes de votre base de données;

    • les deux <tr> à la fin de l’exemple correspondent respectivement à la 1ère ligne de la table (avec les noms des colonnes), et aux lignes contenant les cours.

  • Dans le TypeScript :

    • PeriodicElement correspond à votre interface Cours;

    • ELEMENT_DATA est le tableau de data retourné par votre backend;

    • TableBasicExample correspond à CoursComponent;

    • dans displayedColumns, on indique les noms des colonnes de la base de données que l’on souhaite afficher.

Modifiez votre composant CoursComponent de manière à utiliser une table Angular Material plutôt qu’une simple table HTML. Si tout se passe bien, vous verrez probablement apparaître dans la console Javascript un message d’erreur « Can’t bind to “matHeaderRowDef”….. ». En effet, Angular Material semble encore utiliser la notion de module Angular (qui a été remplacé dans tout ce que vous avez réalisé jusqu’à maintenant par du code « standalone »). Bref, pas de panique, dans la documentation d’Angular Material, vous pouvez voir à côté de l’onglet « OVERVIEW » (en haut de la page) un onglet « API ». Cliquez dessus. Il vous indique l’import que vous devez rajouter dans votre composant CoursComponent. Attention : cela signifie qu’il faut ajouter MatTableModule au tableau d’imports de l’annotation @Component({ et également ajouter import {MatTableModule} from '@angular/material/table'; en haut de votre fichier cours.component.ts.

Exercice 3 : Forum - Pagination   

Dans le forum de démonstration, vous pouvez observer qu’en bas des tables, il y a un composant permettant de découper la table en plusieurs pages (un paginateur). Pour en rajouter un, observez l’exemple de l’URL :

https://material.angular.io/components/table/overview#pagination

Là encore, lisez bien l’exemple puis dévoilez l’indice ci-dessous pour voir si vous avez bien compris.

Indice 1 

  • Dans le template HTML :

    • Seule la balise mat-paginator a besoin d’être ajoutée après la table.

  • Dans le TypeScript :

    • il faut ajouter MatPaginatorModule dans la liste des modules à importer;

    • dans la classe CoursComponent, il faut redéfinir le dataSource :

      dataSource = new MatTableDataSource<Cours>([]);
      
    • la documentation dit d’ajouter aussi :

      @ViewChild(MatPaginator) paginator!: MatPaginator;
      

      Cela servira dans le ngOnInit() pour affecter le paginator à la table. @ViewChild est progressivement remplacé par l’opérateur viewChild() des view queries (cf. https://angular.dev/guide/components/queries). Je vous suggère donc d’utiliser plutôt le code suivant :

      paginator = viewChild(MatPaginator);
      
    • dans la méthode ngOnInit(), après avoir récupéré les données backend, on initialise le datasource et le paginator :

      this.dataSource.data = res.data;
      this.dataSource.paginator = this.paginator;
      

      res est l’objet retourné par votre MessageService. Ici, notez que c’est bien la propriété data du dataSource qu’il faut affecter. Ensuite, on affecte l’attribut paginator de la classe CoursComponent, c’est-à-dire le vrai paginateur du template HTML, à la table Angular Material. Si vous avez utilisé la version « moderne » avec viewChild(), cela devient :

      this.dataSource.data = res.data;
      this.dataSource.paginator = this.paginator() ?? null;
      

      Avec viewChild(), le this.paginator() peut éventuellement être undefined. Mais le type de this.dataSource.paginator est MatPaginator | null, pas MatPaginator | undefined. Pour pallier cela, j’ai écrit ?? null. L’opérateur ?? évalue this.paginator(). S’il est différent de null et de undefined, l’expression est égale à ce qui précède le ??, donc à this.paginator(), sinon elle est égale à ce qui suit le ??, donc à null.

Si vous avez bien réalisé ces quelques modifications, dans la console, vous devriez voir un message d’erreur vous indiquant qu’il faudrait déclarer provideAnimationsAsync(), provideAnimations() ou provideNoopAnimations(). Il faut le faire dans le fichier app.config.ts. Rajoutez-y le provideAnimations(). Normalement, vous devriez voir votre paginateur et celui-ci devrait fonctionner correctement.

Exercice 4 : Forum - Tri des colonnes (optionnel)   

L’URL ci-dessous vous montre comment trier les colonnes de vos tables Angular Material.

https://material.angular.io/components/table/overview#sorting

Là encore, lisez l’exemple du site d’Angular Material puis dévoilez l’indice ci-dessous.

Indice 1 

  • Dans le template HTML :

    • il faut ajouter matSort dans la balise <table>;

    • il faut ajouter mat-sort-header dans chaque balise <th> que l’on souhaite pouvoir trier.

  • Dans le TypeScript :

    • il faut ajouter MatSortModule à la liste des imports;

    • il faut ajouter @ViewChild(MatSort) sort!: MatSort; ou sort = viewChild(MatSort); dans la classe CoursComponent;

    • dans le ngOnInit(), il faut ajouter this.dataSource.sort = this.sort; ou this.dataSource.sort = this.sort() ?? null;.

Exercice 5 : Forum - Des boutons stylés (optionnel)   

En haut de la page des topics, vous avez un bouton qui permet de rajouter de nouveaux sujets. L’URL ci-dessous vous montre comment le transformer en bouton Angular Material.

https://material.angular.io/components/button/overview

Vous pouvez redéfinir les couleurs de votre bouton via les propriétés CSS color ou background-color ou, si vous souhaitez modifier tous les boutons de votre application, en regardant l’onglet « STYLING » de l’URL ci-dessus.

Exercice 6 : Forum - Fenêtre modale Angular (optionnel)   

Pour la boite de dialogue permettant de créer un nouveau sujet, vous avez utilisé la balise <dialog> de HTML. Angular Material propose également une boite de dialogue, cf. l’URL :

https://material.angular.io/components/dialog/overview

Là encore, lisez les exemples de cette page puis dévoilez l’indice ci-dessous.

Indice 1 

  • Il faut créer un nouveau composant qui contiendra l’intérieur de la boite de dialogue. Appelons-le topic-dialog.

  • Dans le template HTML du CreateTopicDialogComponent :

    • Le contenu de la balise <dialog> doit être transféré dans le template HTML de topic-dialog. Et la balise <dialog></dialog> doit être supprimée.

    • le bouton qui ouvrait la fenêtre doit appeler une seule méthode openDialog().

  • Dans le TypeScript du CreateTopicDialogComponent :

    • la classe DialogOverviewExample d’Angular Material correspond à votre CreateTopicDialogComponent.

    • la classe DialogOverviewExampleDialog d’Angular Material correspond à votre TopicDialogComponent.

    • la propriété data passée en argument de this.dialog.open contient toutes les données que vous souhaitez transmettre au popup (ici, seul l’ID du cours est utile).

    • la méthode createTopic() devrait être déplacée dans le TypeScript de TopicDialogComponent.

    • dans le Typescript, il ne devrait plus rester qu’une méthode openDialog() similaire à celle de la page d’Angular Material.

  • Dans le HTML topic-dialog.component.html :

    • il est inutile, ici, d’utiliser les <mat-form-field>, ce que vous avez écrit fonctionne déjà bien. Vous pourrez juste associer au bouton de fermeture sans création de nouveau sujet une méthode onCancel() qui se contentera d’exécuter un this.dialogRef.close();, comme dans l’exemple d’Angular Material.

  • Dans le TypeScript topic-dialog.component.ts :

    • il faut modifier la méthode createTopic() de manière à ce qu’elle n’émette pas le résultat de l’insertion mais plutôt qu’elle fasse un this.dialogRef.close(résultat);, où résultat est ce que l’on souhaite transmettre aux classes parentes (ici, le bouton CreateTopicDialogComponent). Ce bouton pourra alors émettre le résultat à son parent TopicsComponent.

 
© C.G. 2007 - 2025