Cours 420-2D7-LL Développement web

*** lundi, 20 avril 2026 --> semaine 12 !!! ***



Semaine 9 - Les Services et le Routing

 

Télécharger : 5 - Théorie Semaine 9 (Structure du code avec les Services).docx

  

 

 

*ngIf sera bientôt déprécié "Depreciated"

Remplacer le vieux code dans le à marques-voitures.html par : (@if)

    <!-- <div style="width:20px;height:20px;background-color:red;"

    *ngIf="voituresStatus === 'Non disponible'"></div> -->

 

    @if (voituresStatus === 'Non disponible') {

       <div style="width:20px;height:20px;background-color:red;"></div>

    } @else if (voituresStatus === 'Disponible') {

       <div style="width:20px;height:20px;background-color:green;"></div>

    } @else {

        <!-- Rien du tout -->

    }

 

 

*ngFor sera bientôt déprécié "Depreciated"

Remplacer le vieux code dans le à app.html par : (@for)

        <!--<app-marques-voitures *ngFor="let totovoiture of voitures"

                              [voituresName]="totovoiture.name"

                              [voituresStatus]="totovoiture.status"></app-marques-voitures> -->

           @for (item of voitures; track item.name) {

            <app-marques-voitures

              [voituresName] =  item.name

              [voituresStatus] = item.status >

            </app-marques-voitures> }

 

 

5 - Améliorez la structure du code avec les Services

                                                                                                                                                    

Partie 5 (Voitures9)

 

1.       Les Services

Qu'est-ce qu'un service ?

Dit très simplement, un service permet de centraliser des parties de votre code et des données qui sont utilisées par plusieurs parties de votre application ou de manière globale par l'application entière.  Les services permettent donc :

·        de ne pas avoir le même code doublé ou triplé à différents niveaux de l'application - ça facilite donc la maintenance, la lisibilité et la stabilité du code ;

·        de ne pas copier inutilement des données - si tout est centralisé, chaque partie de l'application aura accès aux mêmes informations, évitant beaucoup d'erreurs potentielles.

Dans le cas de l'application que vous avez créée lors des derniers chapitres, on pourrait imaginer un service  VoitureService  qui contiendrait les données des voitures, et également des fonctions globales liées aux voitures, comme tout rendre Disponible ou Non disponible que vous pourrez enfin intégrer.  L'authentification reste simulée pour l'instant, mais quand vous l'intégrerez, on pourrait imaginer un deuxième service  AuthService  qui s'occuperait de vérifier l'authentification de l'utilisateur, et qui pourrait également stocker des informations sur l'utilisateur actif comme son adresse mail et son pseudo.

Injection et instances

Pour être utilisé dans l'application, un service doit être injecté, et le niveau choisi pour l'injection est très important.  Il y a trois niveaux possibles pour cette injection :

·        Dans  AppModule  : ainsi, la même instance du service sera utilisée par tous les components de l'application et par les autres services ;

·        Dans  AppComponent  : comme ci-dessus, tous les components auront accès à la même instance du service mais non les autres services ;

·        Dans un autre component : le component lui-même et tous ses enfants (c'est-à-dire tous les components qu'il englobe) auront accès à la même instance du service, mais le reste de l'application n'y aura pas accès.

Pour les exemples de ce cours, vous injecterez systématiquement les services dans  AppModule  AppComponent pour rendre disponible une seule instance par service à toutes les autres parties de votre application.

 

 

 

Vous y intégrerez bientôt des données et des fonctions, mais pour l'instant, vous allez injecter ce service dans  AppComponent en l'ajoutant au constructor et n'oubliez pas d'ajouter l'import correspondant en haut du fichier :

import { ServiceVoiture } from './services/service-voiture';

 

@Component({

  selector: 'app-root',

  imports: [MonPremier,MarquesVoitures,CommonModule],

  templateUrl: './app.html',

  styleUrl: './app.scss'

})

export class App {

  title = 'Voitures8 de Mers 2026';

  isAuth = false;

  lastUpdate = new Observable((observer) => {

    //setTimeout(() => observer.next(new Date().toString()), 1000);

    //if (typeof window !== 'undefined') {

    //  window.setInterval(()=>{observer.next(new Date().toString())},1000);

    //}

    if (typeof window !== 'undefined') {

      window.setInterval(()=>{observer.next(new Date().toLocaleString("fr-CA"))},1000);

    }

  });

 

  voitures = [

    {

      name: 'Bugatti',

      status: 'Non disponible'

    },

    {

      name: 'Ferrari',

      status: 'Disponible'

    },

    {

      name: 'Lambhorgini',

      status: 'Non disponible'

    }

  ];

 

  constructor(private cdr: ChangeDetectorRef, private service:ServiceVoiture) {

 

    setTimeout(

      () => {

 

          this.isAuth = true;

          this.cdr.detectChanges();

 

      }, 4000

    );

  }

Angular crée maintenant une instance du service  ServiceVoiture. Pour l'intégrer dans un component, on le déclare comme argument dans son constructeur. Vous pouvez l'intégrez-le dans un de vos autres component (sans oublier d'ajouter l'import en haut):

Exemple pour Moi = mon-premier2.component.ts

 

Maintenant, dans AppComponent ou plus tard, dans votre component ( moi = mon-premier2.component.ts),  vous avez un membre appelé  VoitureService  qui correspond à l'instance de ce service que vous avez créé dans  AppComponent .  Vous y ajouterez de la fonctionnalité dans le chapitre suivant.

Utilisez les Services

Le premier élément qu'il serait logique de déporter dans le service serait l'array Voitures.  Copiez-le depuis  AppComponent , collez-le dans  VoitureService  et, de nouveau dans  AppComponent , déclarez  voitures  simplement comme un array de type  any  :

Dans service-voiture.service.ts:

 

import { Injectable } from '@angular/core';
 
@Injectable({
  providedIn: 'root',
})
export class ServiceVoiture {
 
  voitures = [
    {
      name: 'Bugatti',
      status: 'Non disponible'
    },
    {
      name: 'Ferrari',
      status: 'Disponible'
    },
    {
      name: 'Lambhorgini',
      status: 'Non disponible'
    }
  ]; 
  constructor() { }
}
 
Dans app.component.ts : ( remplacer le Tableau par à )
 

export class App {

  title = 'Voitures9 de Mers 2026';

  isAuth = false;

  lastUpdate = new Observable((observer) => {

    //setTimeout(() => observer.next(new Date().toString()), 1000);

    //if (typeof window !== 'undefined') {

    //  window.setInterval(()=>{observer.next(new Date().toString())},1000);

    //}

    if (typeof window !== 'undefined') {

      window.setInterval(()=>{observer.next(new Date().toLocaleString("fr-CA"))},1000);

    }

  });

 

  voitures : any;

 

  Il faut maintenant que  AppComponent  puisse récupérer les informations stockées dans  VoitureService .  Pour cela, vous allez implémenter la méthode  ngOnInit() .

ngOnInit()  correspond à une "lifecycle hook". Le détail de ces hooks va au-delà du cadre de ce cours, mais pour l'instant, tout ce que vous avez besoin de savoir, c'est que la méthode  ngOnInit()  d'un component est exécutée une fois par instance au moment de la création du component par Angular, et après son constructeur. On l'utilise très souvent pour initialiser des données une fois le component créé. Plus tard dans cette partie du cours, vous découvrirez également  ngOnDestroy() . Pour plus d'informations, référez-vous à la documentation d'Angular.

Pour ce faire, vous allez d'abord créer la fonction  ngOnInit()  - généralement on la place après le constructeur et avant les autres méthodes du component : (si elle n'est pas déjà présente !!!)

Dans app.component.ts :
 

   voitures : any;

 

  constructor(private cdr: ChangeDetectorRef, private service:ServiceVoiture) {

 

    setTimeout(

      () => {

 

          this.isAuth = true;

          this.cdr.detectChanges();

 

      }, 4000

    );

  }

    ngOnInit() {

  }

  onAllumer() {

    console.log('On rend tout disponible !');

  }

}

 

Ensuite, dans la déclaration de classe  AppComponent , vous allez implémenter l'interface  OnInit  (en l'important depuis  @angular/core  en haut) :

 

import { Component, OnInit} from '@angular/core';

import { MonPremier } from './mon-premier/mon-premier';

import { MarquesVoitures } from './marques-voitures/marques-voitures';

import { ChangeDetectorRef } from '@angular/core';

import { CommonModule } from '@angular/common';

import { Observable } from 'rxjs';

import { ServiceVoiture } from './services/service-voiture';

 

@Component({

  selector: 'app-root',

  imports: [MonPremier,MarquesVoitures,CommonModule],

  templateUrl: './app.html',

  styleUrl: './app.scss'

})

export class App implements OnInit{

  title = 'Voitures9 de Mers 2026';

  isAuth = false;

 

Vous pouvez maintenant récupérer les informations depuis VoitureService  dans la méthode  ngOnInit() de app.ts :

 

  constructor(private cdr: ChangeDetectorRef, private service:ServiceVoiture) {

 

    setTimeout(

      () => {

 

          this.isAuth = true;

          this.cdr.detectChanges();

 

      }, 4000

    );

  }

    ngOnInit() {

        this.voitures = this.service.voitures;

  }

 


La liaison directe à un array comme ici n'est généralement pas la meilleure des pratiques.  J'ai choisi d'employer cette méthode ici pour montrer plus simplement l'intégration des services, mais ne vous en faites pas : nous verrons les meilleures méthodes plus tard dans ce cours .

 

Votre application devrait fonctionner à nouveau, avec la liste des voitures qui s'affiche comme avant.  Il n'y a aucune différence visuelle, mais votre code est maintenant plus modulaire, et ce sera plus facile d'ajouter des fonctionnalités.  Par exemple, vous allez pouvoir créer deux nouvelles méthodes :  switchOnAll()  et  switchOffAll()  pour rendre Disponible ou Non disponible tous les voitures d'un coup.

Commencez par préparer ces méthodes dans service-voiture.service.ts:

 

  constructor() { }

    switchOnAll() {

    for(let voiture of this.voitures) {

      voiture.status = 'Disponible';

    }

  }

 

  switchOffAll() {

    for(let voiture of this.voitures) {

      voiture.status = 'Non disponible';

    }

  }

}

Puis ajoutez un deuxième bouton dans le template(.html) de  AppComponent  :

(Le deuxième bouton devrait déjà être ajouter depuis la dernière partie du TP4 !!!)

    

      <button class="btn btn-success"

      [disabled]="!isAuth"

      (click)="onAllumer()">Tous disponible</button>

      <button class="btn btn-danger"

      [disabled]="!isAuth"

      (click)="onFermer()">Tous Non disponible</button>

 

Enfin, il ne vous reste plus qu'à capturer les événements click dans AppComponent  pour ensuite déclencher les méthodes dans  ServiceVoitureService .  Commencez déjà par onAllumer()  AJOUTER dans app.component.ts:

 

  onAllumer() {

      if(confirm('Etes-vous sûr de vouloir rendre tous vos voitures Disponible ?')) {

        this.service.switchOnAll();

      } else {

        return;

      }

  }

 

Ensuite, pour  onFermer() , vous allez d'abord afficher un message de confirmation pour vous assurer que l'utilisateur est certain de vouloir rendre tout Non disponible :

 

  onFermer() {

    if(confirm('Etes-vous sûr de vouloir rendre tous vos voitures Non disponible ?')) {

      this.service.switchOffAll();    } else {

      return;

   }

 }

 

Vos boutons rendent Disponible ou Non disponible tous les voitures grâce à la communication entre votre  AppComponent  et votre  ServiceVoitureService .

Mais j'aurais pu faire tout ça à l'intérieur du component - quel est l'intérêt d'avoir tout mis dans un service ?

Effectivement, les fonctionnalités que vous avez ajoutées pour l'instant auraient pu rester dans  AppComponent , mais dans le chapitre suivant, vous allez profiter du service pour créer de la communication entre vos components, notamment des components enfants vers leur parent.

2.     Faites communiquer vos components

Pour l'instant, votre utilisateur ne peut que rendre Disponible ou Non disponible tous les voitures à la fois.  Ce qui pourrait être très intéressant, ce serait qu'il puisse en rendre Disponible ou Non disponible un à la fois.  Actuellement, le plan de l'application ressemble à ça :

ServiceVoitureService fournit les données sur les voitures à  AppComponent .  Ensuite, AppComponent génère trois instances de VoitureComponent selon ces données.  Il n'y a actuellement aucune communication entre les components enfants et leur parent.  Vous pouvez modifier cela en intégrant  ServiceVoitureService  dans les  VoitureComponent  et en créant des méthodes qui permettent de modifier une voiture à la fois.  Procédez étape par étape.

Dans un premier temps, il faudra que chaque instancede VoitureComponent  puisse dire à  ServiceVoitureService  à quel membre de l'array  voitures  elle correspond.  Heureusement, Angular nous permet de faire ça facilement.  Dans la directive *ngFor, maintenant @for, ajoutez dans app.component.html :

 

           @for (item of voitures; track item.name; let i = $index) {

            <app-marques-voitures

              [voituresName] =  item.name

              [voituresStatus] = item.status >

            </app-marques-voitures> }

 

Cette commande rend disponible l'index de l'objet voiture dans l'array voitures.  Ensuite, il faut pouvoir capturer et travailler avec cette variable : vous pouvez utiliser le property binding.  Pour cela, ajoutez un membre index  au component en tant export

 

Dans marques-voitures.component.ts :

 

export class MarquesVoitures implements OnInit{

 

  @Input() voituresName: string = 'Bugatti';

  @Input() voituresStatus: string = 'Non disponible';

  @Input() indexdesVoiture: number = 5;

 

  constructor() { }

 

Puis liez-y l'index i depuis le template  (app.component.html) :

 

           @for (item of voitures; track item.name; let i = $index) {

            <app-marques-voitures

              [voituresName] =  item.name

              [voituresStatus] = item.status

              [indexdesVoiture] = i >

            </app-marques-voitures> }

 

À partir de là, vous avez une variable index disponible à l'intérieur du component qui correspond à l'index de la voiture dans l'array de VoitureService.  Vous verrez dans quelques instants pourquoi vous en avez besoin. 

Dans VoitureService , vous allez maintenant créer les méthodes permettant de rendre Disponible ou Non disponible une seul voiture en fonction de son index dans l'array  voitures  , service-voiture.service.ts:

AJOUTER à

      switchOnOne(index: number) {

        this.voitures[index].status = 'Disponible';

      }

   

      switchOffOne(index: number) {

          this.voitures[index].status = 'Non disponible';

      }

Ensuite, dans  MarqueVoitureComponent , vous allez d'abord intégrer le service  ServiceVoitureService, en l'important en haut du fichier comme toujours dans marques-voitures.component.ts:

Puis vous allez préparer la méthode qui, en fonction du statut actuel de l'appareil, le rendra Disponible ou Non disponible:

 

import { Component, Input, OnInit } from '@angular/core';

import { FormsModule} from '@angular/forms';

import { CommonModule } from '@angular/common';

import { ServiceVoiture} from '../services/service-voiture';

 

@Component({

  selector: 'app-marques-voitures',

  imports: [FormsModule,CommonModule],

  templateUrl: './marques-voitures.html',

  styleUrl: './marques-voitures.scss',

})

export class MarquesVoitures implements OnInit{

 

  @Input() voituresName: string = 'Bugatti';

  @Input() voituresStatus: string = 'Non disponible';

  @Input() indexdesVoiture: number = 5;

 

  constructor(private service:ServiceVoiture) { }

  ngOnInit(): void {

  }

  getStatus() {

    return this.voituresStatus;

  }

  getColor() {

    if(this.voituresStatus === 'Non disponible'){

      return 'red';

    }

    else if(this.voituresStatus === 'Disponible'){

      return 'green';

    }

    else {return 'black'}

  }

 

  onSwitchOn() {

    this.service.switchOnOne(this.indexdesVoiture);

  }

 

  onSwitchOff() {

    this.service.switchOffOne(this.indexdesVoiture);

    }  

}

 

Le nom onSwitch()  ici est choisi pour respecter la norme d'employer "on" pour la capture d'un événement, et non pour dire "switch on" comme "Disponible".

Enfin, vous allez créer le bouton dans le template qui déclenchera cette méthode.  Il serait intéressant que ce bouton soit contextuel : si la voiture est Disponible, il affichera "Non disponible" et inversement.  Pour cela, le plus simple est de créer deux boutons dotés de la directive *ngIf dans marques-voitures.component.html

 

        <h4 [ngStyle]="{color: getColor()}">Voiture : {{ voituresName }} -- Statut : {{ getStatus() }}</h4>

        <a [routerLink]=[id]>Détail</a>

   

    <input type="text" class="form-control" [(ngModel)]="voituresName">

    <input type="text" class="form-control" [(ngModel)]="voituresStatus">

 

    @if (voituresStatus === 'Non disponible') {

       <button class="btn btn-sm btn-success" (click)="onSwitchOn()">Disponible</button>

    }

    @if (voituresStatus === 'Disponible') {

       <button class="btn btn-sm btn-danger" (click)="onSwitchOff()">Non disponible</button>

    }

</li>

 

 

Vous pouvez supprimer la <div> conditionnelle rouge, car elle ne sert plus vraiment, avec tous les styles que vous avez ajoutés pour signaler l'état d'une voiture ou non!

 Et voilà !  Vos components communiquent entre eux à l'aide du service, qui centralise les données et certaines fonctionnalités.  Même si les effets ne sont que visuels pour l'instant, vous pouvez très bien imaginer qu'à l'intérieur des méthodes du service VoitureService ,il y ait des appels API permettant de vraiment rendre Disponible ou Non disponible les voitures et d'en vérifier le fonctionnement.

Réf. : https://openclassrooms.com/fr/courses/4668271-developpez-des-applications-web-avec-angular/5088601-ameliorez-la-structure-du-code-avec-les-services

Réf. : https://www.itsolutionstuff.com/post/how-to-create-service-in-angular-17example.html

 


 

Télécharger : 6 - Théorie Semaine 9 (Gérez la navigation avec le Routing).docx

 

6 - Gérez la navigation avec le Routing                                                                                                                                         

 

Partie 6 (Voitures10)

 

1.  Gérez la navigation avec le Routing

L'un des énormes avantages d'utiliser Angular est de pouvoir créer des "single page application" (SPA). Sur le Web, ces applications sont rapides et lisses : il n'y a qu'un seul chargement de page au début, et même si les données mettent parfois du temps à arriver, la sensation pour l'utilisateur est celle d'une application native.  Au lieu de charger une nouvelle page à chaque clic ou à chaque changement d'URL, on remplace le contenu ou une partie du contenu de la page : on modifie les components qui y sont affichés, ou le contenu de ces components.  On accomplit tout cela avec le "routing", où l'application lit le contenu de l'URL pour afficher le ou les components requis.

L'application des voitures n'a que la view des voitures à afficher pour le moment ; je vous propose de créer un component pour l'authentification (qui restera simulée pour l'instant) et vous créerez un menu permettant de naviguer entre les views.

Tout d'abord, créez le component avec le CLI :

sudo ng g c auth

Vous allez également devoir modifier un peu l'organisation actuelle afin d'intégrer plus facilement le routing : vous allez créer un component qui contiendra toute la view actuelle et qui s'appellera  VoitureViewComponent  :

sudo ng g c voiture-view

Ensuite, coupez tout le contenu de la colonne de app.component.html, enregistrez-le dans voiture-view.component.html

 

 

 , et remplacez-le par la nouvelle balise <app-voiture-view> dans app.component.html  :

 

<div style="text-align:center">

  <h1>

    Bienvenue aux {{ title }}!

  </h1>

</div>

<app-mon-premier></app-mon-premier>

 

Il faudra également déménager la logique de cette view (app.component.ts) pour que tout fonctionne à nouveau : injectez  ServiceVoitureService "mettre les bons import dans le haut du fichier"  (et  , transférer l'array  voitures , isAuth, lastUpdate,  intégrez la logique  ngOnInit  et déplacez les fonctions  onAllumer()  et  onFermer() dans voiture-view.component.ts :

Mon résultat:

import { Component, OnInit } from '@angular/core';

import { ServiceVoiture } from '../services/service-voiture';

import { MarquesVoitures } from '../marques-voitures/marques-voitures';

import { ChangeDetectorRef } from '@angular/core';

import { CommonModule } from '@angular/common';

import { Observable } from 'rxjs';

 

@Component({

  selector: 'app-voiture-view',

  imports: [MarquesVoitures, CommonModule],

  templateUrl: './voiture-view.html',

  styleUrl: './voiture-view.scss',

})

export class VoitureView implements OnInit{

  isAuth = false;

  voitures : any;

  lastUpdate = new Observable((observer) => {

    //setTimeout(() => observer.next(new Date().toString()), 1000);

    //if (typeof window !== 'undefined') {

    //  window.setInterval(()=>{observer.next(new Date().toString())},1000);

    //}

    if (typeof window !== 'undefined') {

      window.setInterval(()=>{observer.next(new Date().toLocaleString("fr-CA"))},1000);

    }

  });

    constructor(private cdr: ChangeDetectorRef, private service:ServiceVoiture) {

 

    setTimeout(

      () => {

 

          this.isAuth = true;

          this.cdr.detectChanges();

 

      }, 4000

    );

  }

 

  ngOnInit() {

        this.voitures = this.service.voitures;

  }

  onAllumer() {

      if(confirm('Etes-vous sûr de vouloir rendre tous vos voitures Disponible ?')) {

        this.service.switchOnAll();

      } else {

        return;

      }

  }

 

  onFermer() {

    if(confirm('Etes-vous sûr de vouloir rendre tous vos voitures Non disponible ?')) {

      this.service.switchOffAll();    } else {

      return;

   }

 }

 

}

 

 

Vous pouvez faire le ménage dans AppComponent , en retirant tout ce qui n'y sert plus.  Assurez-vous d'avoir également une boolean  isAuth  dans  VoitureViewComponent , et déclarez-la comme  false , car vous allez intégrer un service d'authentification pour la suite.

Après le ménage, voici le contenue de mon AppComponent (app.component.ts):

 

import { Component} from '@angular/core';

import { VoitureView } from './voiture-view/voiture-view';

import { CommonModule } from '@angular/common';

 

 

@Component({

  selector: 'app-root',

  imports: [CommonModule,VoitureView],

  templateUrl: './app.html',

  styleUrl: './app.scss'

})

export class App {

  title = 'Voitures10 de rêve de Mers 2026';

}

 

*** Notez que j'ai également enlever ce qui avait rapport avec "mon-premier" car je ne vais plus l'utiliser avant quelques semaines.

 

Ajoutez la barre de navigation suivante à  AppComponent (.html)  :  

<div style="text-align:center">

  <h1>

    Bienvenue aux {{ title }}!

  </h1>

</div>

<HR>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">

       

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">

        <span class="navbar-toggler-icon"></span>

      </button>

      <div class="collapse navbar-collapse" id="navbarNav">

        <ul class="navbar-nav">

          <li class="active"><a href="#">Voitures</a></li>

          <li>

          <a href="#">>Authentification</a>

          </li>

        </ul>

      </div>

    </nav>

<HR>

  <div class="container">

    <div class="row">

      <div class="col-xs-12">

        <app-voiture-view></app-voiture-view>

      </div>

    </div>

  </div>

 

Maintenant, tout est prêt pour créer le routing de l'application.

 

2.     Créez des routes

 

Tout d'abord, qu'est-ce qu'une route dans une application Angular ?

Il s'agit des instructions d'affichage à suivre pour chaque URL, c'est-à-dire quel(s) component(s) il faut afficher à quel(s) endroit(s) pour un URL donné.

Puisque le routing d'une application est fondamentale pour son fonctionnement, on déclare les routes dans  app.routes.ts

Il est possible d'avoir un fichier séparé pour le routing, mais en termes de fonctionnalité, cela ne change rien : c'est juste une question d'organisation du code.

On crée une constante de type Routes (qu'on importe depuis @angular/router ) qui est un array d'objets JS qui prennent une certaine forme :

import { Routes } from '@angular/router';
import { Auth } from './auth/auth';
import { VoitureView } from './voiture-view/voiture-view';

Les routes sont maintenant créées, mais il faut les enregistrer dans votre application.  Pour cela, vous allez importer RouterModule depuis @angular/router et vous allez l'ajouter à l'array imports de votre AppComponent, tout en lui appelant la méthode forRoot()  en lui passant l'array de routes que vous venez de créer :

import { Routes, RouterModule } from '@angular/router';

import { CommonModule } from '@angular/common';

import { RouterModule } from '@angular/router';

import { VoitureViewComponent } from './voiture-view/voiture-view.component'

 

@Component({

  selector: 'app-root',

  standalone: true,

  imports: [RouterOutlet, MonPremierComponent, MonPremier2Component, CommonModule, VoitureViewComponent, RouterModule],

 

Maintenant que les routes sont enregistrées, il ne reste plus qu'à dire à Angular où vous souhaitez afficher les components dans le template (app.component.html) lorsque l'utilisateur navigue vers la route en question.  On utilise la balise <router-outlet> :

<HR> 
   <div class="container">
      <div class="row">
      <div class="col-xs-12">
         <router-outlet></router-outlet>
      </div>
      </div>
   </div>
<!-- <app-voiture-view></app-voiture-view> -->
<app-coordonee></app-coordonee>
<app-pied></app-pied>
 

Lorsque vous changez de route (pour l'instant, en modifiant l'URL directement dans la barre d'adresse du navigateur), la page n'est pas rechargée, mais le contenu sous la barre de navigation change.  Dans le chapitre suivant, vous allez intégrer les liens de la barre de navigation afin que l'utilisateur puisse naviguer facilement.

 

3.  Naviguez avec les routerLink

 

Afin que l'utilisateur puisse naviguer à l'intérieur de votre application, il est nécessaire de créer des liens ou des boutons qui naviguent vers les routes que vous avez créées.  Dans le chapitre précédent, vous avez créé des liens typiques dans la barre de navigation, mais qui ne font rien pour l'instant.

Vous pourriez vous dire qu'il suffirait de marquer le  path  de vos routes directement dans l'attribut  href , et techniquement, cela permet d'atteindre les routes que vous avez créées.

Alors pourquoi on ne fait pas comme ça ?

Tout simplement parce que, si vous regardez bien, en employant cette technique, la page est rechargée à chaque clic

On perd totalement l'intérêt d'une Single Page App !

Du coup, on retire l'attribut href et on le remplace par l'attribut routerLink :

Dans app.component.html à

 
<HR>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">  
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
      </button>
        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item active">
              <a class="nav-link" routerLinkActive="active" routerLink="voitures">Voitures</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" routerLinkActive="active" routerLink="auth">Authentification</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled" routerLinkActive="active" href="#">Disabled</a>
            </li>       
          </ul>
        </div>
    </nav>
 
<HR>
 

Ainsi, les routes sont chargées instantanément : on conserve l'ergonomie d'une SPA.

Pour finaliser cette étape, il serait intéressant que la classe active ne s'applique qu'au lien du component réellement actif.  Heureusement, Angular fournit un attribut pour cela qui peut être ajouté au lien directement ou à son élément parent :

Maintenant, les liens s'activent visuellement.

 

4.  Naviguez avec le Router

 

Il peut y avoir des cas où vous aurez besoin d'exécuter du code avant une navigation.  Par exemple, on peut avoir besoin d'authentifier un utilisateur et, si l'authentification fonctionne, de naviguer vers la page que l'utilisateur souhaite voir.  Je vous propose d'intégrer cette fonctionnalité à l'application des voitures électriques (l'authentification elle-même restera simulée pour l'instant).

Tout d'abord, créez un nouveau fichier auth.service.ts service-auth.service.ts dans le dossier services pour gérer l'authentification (n'oubliez pas de l'ajouter également dans dans  AppComponent ) :

Créer un Service appelé "service-auth" dans mon cas:

 

ng generate service service-auth

 
Copier le nouveaux fichier dans le dossier service.
 
Ensuite, dans app.component.ts :
 
import { ServiceAuth } from './services/service-auth'
 
et
 
export class App {
  title = 'Voitures10 de rêve de Mers 2026';
  constructor(private service: ServiceAuth){
 
  }
}
 
Dans service-auth.service.ts:

 

export class ServiceAuth {

     isAuth = false;

 

    signIn() {

      return new Promise(

        (resolve, reject) => {

          setTimeout(

            () => {

              this.isAuth = true;

              resolve(true);

            }, 2000

          );

        }

      );

    }

 

    signOut() {

      this.isAuth = false;

    }

 

  constructor() { }

}

 

Suggestion si la route ne fonctionne pas bien, redémarrer avec sudo ng serve et redémarrer VS Code. Solution ultime, recréer le fichier auth.service.ts 

La variable isAuth donne l'état d'authentification de l'utilisateur.  La méthode signOut()  "déconnecte" l'utilisateur, et la méthode  signIn()  authentifie automatiquement l'utilisateur au bout de 2 secondes, simulant le délai de communication avec un serveur.

Dans le component auth.component.ts, vous allez simplement créer deux boutons et les méthodes correspondantes pour se connecter et se déconnecter (qui s'afficheront de manière contextuelle : le bouton "se connecter" ne s'affichera que si l'utilisateur est déconnecté et vice versa) :

 
import { Component } from '@angular/core';
import { ServiceAuth } from '../services/service-auth';
 
@Component({
  selector: 'app-auth',
  imports: [],
  templateUrl: './auth.html',
  styleUrl: './auth.scss',
})
export class Auth {
  authStatus: boolean = true;
 
  constructor(private service : ServiceAuth) { }
 
  ngOnInit() {
    this.authStatus = this.service.isAuth;
  }
 
  onSignIn() {
    this.service.signIn().then(
      () => {
        console.log('Sign in successful!');
        this.authStatus = this.service.isAuth;
      }
    );
  }
 
  onSignOut() {
    this.service.signOut();
    this.authStatus = this.service.isAuth;
  }
}
 

Puisque la méthode signIn()  du service retourne une Promise, on peut employer une fonction callback asynchrone avec  .then()  pour exécuter du code une fois la Promise résolue.  Ajoutez simplement les boutons, et tout sera prêt pour intégrer la navigation :

Dans auth.component.html 

 

<h2>Authentification</h2>
    @if (!authStatus) {
       <button class="btn btn-success" (click)="onSignIn()">Se connecter</button> 
    }
    @if (authStatus) {
       <button class="btn btn-danger" (click)="onSignOut()">Se déconnecter</button>
    }

 

<!-- <button class="btn btn-success" *ngIf="!authStatus" (click)="onSignIn()">Se connecter</button>
<button class="btn btn-danger" *ngIf="authStatus" (click)="onSignOut()">Se déconnecter</button> -->
 
*** Ajouter le module nécessaire pour utiliser *ngIf dans le fichier auth.ts correspondant:
 
import { CommonModule } from '@angular/common';
et plus bas …
imports: [CommonModule],

Le comportement recherché serait qu'une fois l'utilisateur authentifié, l'application navigue automatiquement vers la view des voitures.  Pour cela, il faut injecter le  Router  (importé depuis  @angular/router ) pour accéder à la méthode  navigate()  :

Dans auth.component.ts :

 

import { Component } from '@angular/core';

import { ServiceAuth } from '../services/service-auth';

import { CommonModule } from '@angular/common';

import { Router } from '@angular/router';

 

@Component({

  selector: 'app-auth',

  imports: [CommonModule],

  templateUrl: './auth.html',

  styleUrl: './auth.scss',

})

export class Auth {

  authStatus: boolean = true;

 

  constructor(private service : ServiceAuth, private router: Router) { }

 

  ngOnInit() {

    this.authStatus = this.service.isAuth;

  }

 

  onSignIn() {

    this.service.signIn().then(

      () => {

        console.log('Sign in successful!');

        this.authStatus = this.service.isAuth;

        this.router.navigate(['voitures']);

      }

    );

  }

 

  onSignOut() {

    this.service.signOut();

    this.authStatus = this.service.isAuth;

  }

}

 

La fonction navigate prend comme argument un array(tableau) d'éléments (ce qui permet de créer des chemins à partir de variables, par exemple) qui, dans ce cas, n'a qu'un seul membre : le chemin "path" souhaité.

Le chemin voitures est toujours accessible actuellement, même sans authentification : dans un chapitre ultérieur, vous apprendrez à le sécuriser totalement.  Avant cela, vous allez apprendre à ajouter des paramètres à vos routes.

 

 

5.  Paramètres des Routes

 

Imaginez qu'on souhaite pouvoir cliquer sur un voiture dans la liste de voitures afin d'afficher une page avec plus d'informations sur cette voiture : on peut imaginer un système de routing de type voitures/nom-de-la voiture, par exemple.  Si on n'avait que deux ou trois voitures, on pourrait être tenté de créer une route par voiture, mais imaginez un cas de figure où l'on aurait 30 voitures, ou 300.  Imaginez qu'on laisse l'utilisateur créer de nouvelles voitures ; l'approche de créer une route par voiture n'est pas adaptée.  Dans ce genre de cas, on choisira plutôt de créer une route avec paramètre.

Tout d'abord, vous allez créer la route dans    app.routes.ts:

 

const appRoutes: Routes = [
  { path: 'voitures',component: VoitureView },
  { path: 'voitures/:id', component: SingleVoitureComponent },
  { path: 'auth', component: Auth }, 
  { path: '', component: VoitureView },
];
 

L'utilisation des deux-points :  avant un fragment de route déclare ce fragment comme étant un paramètre : tous les chemins de type voitures/* seront renvoyés vers SingleVoitureComponent,  que vous allez maintenant créer :

 

sudo ng g c single-voiture

 

dans single-voiture.component.ts :

 

import { Component } from '@angular/core';

import { ServiceVoiture } from '../services/service-voiture';

import { RouterModule } from '@angular/router';

 

 

@Component({

  selector: 'app-single-voiture',

  imports: [RouterModule],

  templateUrl: './single-voiture.html',

  styleUrl: './single-voiture.scss',

})

export class SingleVoiture {

  name: string = 'Voiture';

  status: string = 'Statut';

 

  constructor(private service:ServiceVoiture) { }

}

 

dans single-voiture.component.html :

<h2>{{ name }}</h2>
<p>Statut : {{ status }}</p>
<a routerLink="/voitures">Retour à la liste</a>
 
Ajouter dans app.routes.ts:

import { SingleVoiture } from './single-voiture/single-voiture';

 

Pour l'instant, si vous naviguez vers /voitures/nom, peu importe le nom que vous choisissez, vous avez accès à  SingleVoitureComponent .

Maintenant, vous allez y injecter ActivatedRoute , importé depuis  @angular/router , afin de récupérer le fragment  id  de l'URL dans single-voiture.component.ts :

 

import { Component } from '@angular/core';

import { ServiceVoiture } from '../services/service-voiture';

import { RouterModule } from '@angular/router';

import { ActivatedRoute } from '@angular/router';

 

 

@Component({

  selector: 'app-single-voiture',

  imports: [RouterModule],

  templateUrl: './single-voiture.html',

  styleUrl: './single-voiture.scss',

})

export class SingleVoiture {

  name: string = 'Voiture';

  status: string = 'Statut';

 

  constructor(private service:ServiceVoiture, private route: ActivatedRoute) { }

}

Puis, dans  ngOnInit() , vous allez utiliser l'objet  snapshot  qui contient les paramètres de l'URL  et, pour l'instant, attribuer le paramètre  id  à la variable  name  : 

  ngOnInit() {
    this.name = this.route.snapshot.params['id'];
  }

Ainsi, le fragment que vous tapez dans la barre d'adresse après voitures/ s'affichera dans le template (html), mais ce n'est pas le comportement recherché.

Pour atteindre l'objectif souhaité, commencez par ajouter, dans   service-voiture.service.ts , un identifiant unique pour chaque voiture et une méthode qui rendra la voiture correspondant à un identifiant :

 

export class VoitureService {
  voitures = [
      {
        id: 1,
        name: 'Bugatti',
        status: 'Non disponible'
      },
      {
        id: 2,
        name: 'Ferrari',
        status: 'Disponible'
      },
      {
        id: 3,
        name: 'Lambhorgini',
        status: 'Non disponible'
      }
    ];
 
      getVoitureById(id: number) {
        const voiture= this.voitures.find(
          (voitureObject) => {
            return voitureObject.id === id;
          }
          
        );
        return voiture;
      }
 

 

Vous pouvez naviguer manuellement vers /voitures/2 , par exemple, mais cela recharge encore la page, et vous perdez l'état des voitures (si vous en rendez Disponible ou Non disponible par exemple).  Pour finaliser cette fonctionnalité, intégrez l'identifiant unique dans MarqueVoitureComponent et dans VoitureViewComponent , puis créez un  "routerLink"  pour chaque voiture qui permet d'en regarder le détail

dans marques-voitures.component.ts

 

  @Input() voituresName: string = 'Bugatti';
  @Input() voituresStatus: string = 'Non disponible';
  @Input() indexdesVoiture: number = 5;
  @Input() id: number = 2;
 
 Et
 
import { Component, Input, OnInit } from '@angular/core';
import { FormsModule} from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ServiceVoiture} from '../services/service-voiture';
import { RouterModule } from '@angular/router';
 
@Component({
  selector: 'app-marques-voitures',
  imports: [FormsModule,CommonModule, RouterModule],
 
Dans voiture-view.component.html
 
        <ul class="list-group">

           @for (item of voitures; track item.name; let i = $index) {

            <app-marques-voitures

              [voituresName] =  item.name

              [voituresStatus] = item.status

              [indexdesVoiture] = i >

            </app-marques-voitures> }

        </ul>
 
Dans voiture-view.component.html 
<ul class="list-group">
            @for (item of voitures; track item.id; let i = $index) {
            <app-marques-voitures
              [voituresName] =  "item.name"
              [voituresStatus] = "item.status"
              [indexdesVoiture] = "i"
              [id] = "item.id" >
            </app-marques-voitures> } 
</ul>
 
 
 
Dans marques-voitures.component.html     
<!-- <div style="width:20px;height:20px;background-color:red;"     
*ngIf="voituresStatus === 'Non disponible'"></div> -->        
 
@if (voituresStatus === 'Non disponible') { 
      <div style="width:20px;height:20px;background-color:red;"></div>
    } @else if (voituresStatus === 'Disponible') {
       <div style="width:20px;height:20px;background-color:green;"></div>
    } @else {        <!-- Rien du tout -->    
}
        <h4 [ngStyle]="{color: getColor()}">Voiture : {{ voituresName }} -- Statut : {{ getStatus() }}</h4>
        <a [routerLink]=[id]>Détail</a>
    <input type="text" class="form-control" [(ngModel)]="voituresName">
    <input type="text" class="form-control" [(ngModel)]="voituresStatus"> 

 

 

Ici, vous utilisez le format array pour routerLink en property binding afin d'accéder à la variable id.

 

Ça y est !  Vous pouvez maintenant accéder à la page Détail pour chaque voiture, et les informations de statut qui s'y trouvent sont automatiquement à jour grâce à l'utilisation du service.

 

 

 

6.  Redirection

 

Il peut y avoir des cas de figure où l'on souhaiterait rediriger un utilisateur, par exemple pour afficher une page 404 lorsqu'il entre une URL qui n'existe pas.

Pour l'application des voitures, commencez par créer un component 404 très simple, appelé four-oh-four.component.ts  :

<h2>Erreur 404</h2>
<p>La page que vous cherchez n'existe pas !</p>
 

Ensuite, vous allez ajouter la route "directe" vers cette page, ainsi qu'une route "wildcard", qui redirigera toute route inconnue vers la page d'erreur :

const appRoutes: Routes = [
  { path: 'appareils', component: AppareilViewComponent },
  { path: 'appareils/:id', component: SingleAppareilComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent },
  { path: 'not-found', component: FourOhFourComponent },
  { path: '**', redirectTo: 'not-found' }
];
 
 

Ainsi, quand vous entrez un chemin dans la barre de navigation qui n'est pas directement pris en charge par votre application, vous êtes redirigé vers /not-found et donc le component 404.

Dans mon cas je redirige vers le component de AuthComponent

App.routes.ts

Page par défaut …

 

 

Réf. : https://openclassrooms.com/fr/courses/4668271-developpez-des-applications-web-avec-angular/5088826-gerez-la-navigation-avec-le-routing

 

 

 

 

 

 


   

Voici mes coordonnees: Stéphane Mercier (Mers), stephane.mercier@cegeplevis.ca, 418 833-5110, poste 5511, Local G205A (disponnible par MIO)

Tout droit réservé à personne !!!

.