Les outputs : interactions enfant -> parent

Lorsqu’un enfant souhaite transmettre une information à son parent, il peut le faire en exploitant l'event binding. Pour cela, 2 étapes sont nécessaires :

  1. dans le TypeScript de l’enfant, créer un output emitter ainsi qu’une méthode qui lui permettra d’émettre une valeur;

  2. dans le template HTML du parent, par event binding dans la balise de l’enfant, récupérer cette valeur.

Pour créer un OutputEmitter, il suffit d’utiliser l’expression output(), comme on l’aurait fait avec un input. L’output ainsi créé possède une méthode emit() qui lui permet de transmettre l’information désirée (cf. la méthode buttonClicked() ci-dessous).

cours.component.ts
import {Component, input, output} from '@angular/core';
import {Course} from '../services/mon-service.service';
import {FormsModule} from '@angular/forms';

@Component({
  selector: 'app-cours',
  imports: [
    FormsModule
  ],
  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
  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>();

  // le nombre qui va se trouver dans l'input
  mynumber = 0;

  // pour transmettre des informations vers le parent, on crée un événement,
  // qua j'ai appelé, ici, myevent. Dans le parent, on n'aura plus qu'à
  // utiliser l'event binding (myevent)="méthode(...)". Le <number> indique
  // que l'information sera un nombre.
  myevent = output<number>();

  buttonClicked() {
    // On émet l'événement myevent. Le parent pourra récupérer la valeur
    // transmise (this.mynumber)
    this.myevent.emit(this.mynumber);
  }
}

Pour tester l’émission de l’événement, j’ai rajouté ci-dessous au template HTML du CoursComponent un input associé à l’attribut this.mynumber et un bouton qui permet de déclencher l’émission de l’événement :

cours.component.html
<ul>
  <li>élément {{index()}} : {{module().nom}} : {{module().nb_etuds}} étuds</li>
</ul>

<input [(ngModel)]="mynumber" />
<button (click)="buttonClicked()">transmettre au parent</button>

On a terminé l’étape 1. Passons à la deuxième, celle où le parent récupère l’événement. Pour cela, dans son template HTML, par event binding, il capture l’événement. Celui-ci a précisément le nom de l’attribut qu’on avait créé avec output().

courses.component.html
<input [(ngModel)]="idMonComp" placeholder="saisir un id"/>
<button (click)="rediriger()">Rediriger vers</button>

@for (module of UE; track module.nom) {
  <app-cours [index]="$index" [module]="module"
             (myevent)="childAfficher($event)" />
}

Notez le $event, c’est un mot-clef qui permet de récupérer la valeur transmise par l’événement. Ici, pour l’afficher, on va simplement utiliser la console, c’est ce que fait la méthode childAfficher() :

courses.component.ts
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';
import {CoursComponent} from '../cours/cours.component';

@Component({
  selector: 'app-courses',
  imports: [
    FormsModule,
    CoursComponent
  ],
  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 = '0';
     this.router.navigateByUrl(`/mon-comp/${this.idMonComp}`);
   }

   childAfficher(nombre: number) {
     console.log(`l'input de l'enfant contient ${nombre}`);
   }
}

Si on utilise les codes ci-dessus, que l’on saisit « 12345 » dans l’input en dessous du 1er « élément » et que l’on clique sur le bouton à sa droite, on verra apparaître le message souligné en rouge dans la console :

Envoi d'une informlation d'un enfant vers un parent

Exercice 1 : Forum - bouton d'ajout de sujet   

Vers le haut de la page des topics se trouve un bouton « nouveau sujet ». Quand on clique dessus, on souhaite que cela ouvre une fenêtre permettant de saisir ce nouveau sujet. Pour cela, créez un nouveau composant create-topic-dialog et ajoutez dedans le bouton « nouveau sujet ». Pour l’instant, le bouton ne fait rien.

Ajoutez une instance du composant CreateDialogComponent dans le fichier topics.component.html.

Exercice 2 : Forum - La fenêtre d'ajout de sujet   

Quand on clique sur le bouton, on souhaite maintenant qu’un popup s’ouvre, nous demandant de saisir le sujet. Il s’agit d’une fenêtre modale :

Règle

Dans le domaine des weberies, on parle de fenêtres de dialogue et de fenêtres modales. Dans les deux cas, l’idée est de faire apparaître par dessus la page web courante une fenêtre qui permet de saisir ou d’afficher des informations. La différence entre une fenêtre modale et une non modale, c’est que la première ne permet plus d’interagir avec la page web tant qu’elle n’est pas fermée tandis que la seconde permet toujours d’interagir avec la page web. Une fenêtre de dialogue peut être modale, on parle alors simplement de fenêtre modale, ou non modale et on la qualifie alors juste de fenêtre de dialogue.

Regardez l’url ci-dessous, qui vous montre comment créer aisément la fenêtre de dialogue. Pour ouvrir une fenêtre modale, il faut simplement remplacer show() par showModal() :

https://blog.angulartraining.com/how-to-create-a-simple-modal-dialog-with-angular-277ea7f96da1

Maintenant, faites en sorte que le bouton « Nouveau sujet » ouvre la fenêtre modale et que celle-ci contienne un bouton permettant de la fermer.

Exercice 3 : Forum - Script d'ajout dans le backend   

Dans votre backend, écrivez un script PHP saveNewTopic.php dont le $_POST est censé contenir une chaîne de caractères non vide (le nouveau sujet) ainsi que l’identifiant du cours auquel ce sujet devrait appartenir. Pour l’instant, le script se contente de renvoyer un message d’erreur contenant cet identifiant ainsi que le sujet.

Exercice 4 : Forum - Ajout et fermeture   

Dans le forum de démonstration, la fenêtre modale est la suivante :

La fenêtre modale d'ajout des topics
  1. Faites en sorte que la vôtre ait un aspect similaire. Notamment, vous voyez qu’il y a deux boutons : « Fermer » qui ferme la fenêtre sans essayer de créer le sujet et « Créer le sujet », qui essaye de le créer. Pour ce dernier, pour l’instant, associez l’événement (click) à l’appel d’une méthode createTopic() qui requête votre script saveNewTopic.php. Si le backend renvoie une erreur, le message d’erreur correspondant apparaît dans la fenêtre modale, sinon aucun message n’apparaît.

  2. Une fois que votre méthode createTopic() fonctionne correctement, rajouter un id="…" dans la balise dialog du fichier create-topic-dialog.component.html, où a la même valeur que le #…. Par exemple :

    <dialog #myDialog id="myDialog">
    

    Pour fermer la fenêtre modale à partir du TypeScript, il suffit alors d’exécuter le code suivant :

    const dialog = document.querySelector<HTMLDialogElement>("#myDialog");
    if (dialog) dialog.close();
    

    Modifiez donc votre composant de manière à ce que, lorsque l’on n’a pas reçu de message d’erreur, la fenêtre modale se ferme (modifiez votre script saveNewTopic.php pour qu’il renvoie un message sans erreur pour tester).

  3. Après que la fenêtre modale a été fermée, rouvrez la. Vous verrez que l’input contient encore le sujet qui avait été tapé précédemment, voire également le message d’erreur s’il y en avait un. Ce n’est pas souhaitable, il faudrait réinitialiser le contenu de la fenêtre à chaque fois qu’on l’ouvre. Pour cela, vous pouvez exploiter le fait qu’il est possible de relier l’événement (click) à l’exécution de plusieurs expressions :

    (click)="méthode1();méthode2()"
    

Exercice 5 : Forum - Récupération du nouveau sujet dans le TopicsComponent   

Dans le composant create-topic-dialog, créez une interface NewTopic :

export interface NewTopic {
  idTopic: number;
  sujet: string;
}

Faites en sorte que, lorsque l’on a réussi à saisir un nouveau sujet, on transmette au composant TopicsComponent le nouveau sujet au format NewTopic. Puis faites en sorte que le TopicsComponent affiche ce nouveau sujet : pour cela, il suffit de rajouter le nouveau sujet au tableau de sujets stocké dans le composant TopicsComponent.

Exercice 6 : Forum - Ajout de nouveaux sujets dans le backend (optionnel)   

Dans votre backend, modifiez le script PHP saveNewTopic.php afin qu’il permette d’ajouter réellement les nouveaux sujets. Pour cela, le script doit vérifier :

  1. que le sujet transmis dans le $_POST est non vide,

  2. que l’utilisateur suit bien le cours,

  3. que le cours ne possède pas déjà ce sujet.

Si au moins une des trois conditions ci-dessus n’est pas vérifiée, le script renvoie un message d’erreur. En revanche, si toutes les conditions sont réunies, le sujet est créé dans la base de données et les propriétés du cours sont mises à jour dans la base (nombre de topics). Enfin, le script renvoie un message contenant l’identifiant du nouveau sujet.

Indice 1 

Pour récupérer l’id du dernier sujet inséré dans la base de données par PDO, l’url suivante pourrait vous être utile :

https://www.php.net/manual/en/pdo.lastinsertid.php

Testez votre script avec Postman.

 
© C.G. 2007 - 2025