Référence : https://angular.io/tutorial/first-app/first-app-lesson-12
Explication sur les événements et le data-binding
Nous avons vu précédemment qu'il est facile de déclarer des propriétés dans la classe d'un composant de manière à les utiliser dans un template par interpolation. Il est également possible de déclarer des propriétés de type "function", soit des méthodes, qui pourront être appelées lorsqu'un événement surviendra. Prenons un exemple simple :
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'test-angular'; handleIntputTitle(inputValue:string) { this.title= inputValue; console.log("Dans handleKeyDown"); } }
Vous voyez que la classe AppComponent possède maintenant une méthode handleIntputTitle.
Voyons comment lier l'appel de cette méthode à l'événement "oninput" dans le template app.component.html :
<div class="toolbar" role="banner"> <h1>{{title}}</h1> <input type="text" [value]="title" (input)="handleIntputTitle(inputTitle.value)" #inputTitle> </div>
Nous utilisons ici deux syntaxes essentielles d'Angular :
- la syntaxe de liaison d'événement Angular. Elle est composée d'un nom d'événement cible entre parenthèses à gauche (input dans notre exemple), d'un signe égal et d'une instruction de modèle entre guillemets à droite (handleIntputTitle dans notre exemple).
- la syntaxe de liaison d'attributs (attribute binding) [value]="title". Elle permet de définir directement les valeurs des attributs. Nous verrons plust tard que la liaison d'attributs permet également d'améliorer l'accessibilité, de styliser votre application de manière dynamique et de gérer plusieurs classes ou styles CSS simultanément.
Vous comprenez en visualisant cet exemple qu'Angular possède un mécanisme interne qui modifie automatique la vue lorsque le modèle (les données) est modifié.
Notez également qu'Angular est sécure dans la mesure où si vos données comportent des instructions js qui pourraient attenter à la sécurité, le code ne sera tout simplement pas interprété. Vous pouvez tester cela en entrant dans l'input :
<script>alert("Attaque la banque de France")</script>
... et vous verrez que votre code js ne sera pas interprété.
La gestion des instances d'Event
Comme un js vanilla, il est possible de récupérer une référence vers l'instance de l'événement qui se propage. Attention cependant car la syntaxe est légèrement différente : il faut utiliser le caractère $ :
<div (click)="onButtonClick($event)"> <button>Click me!</button> </div>
Définition d'un service pour gérer l'événement
Reprenons notre fil conducteur issu du tutorial officiel d'Angular. Le but est d'ajouter un formulaire qui va permettre aux internautes de renseigner un formulaire sans doute dans l'idée de prendre contact avec le propriétaire du logement à louer.
On commence par déclarer la méthode qui va récupérer les données issues du formulaire (il faut bien commencer par quelque chose !).
Dans le fichier src/app/housing.service.ts, coller le code suivant en bas de la définition de la classe :
submitApplication(firstName: string, lastName: string, email: string) { console.log(`Homes application received: firstName: ${firstName}, lastName: ${lastName}, email: ${email}.`); }
On peut se poser la question "Pourquoi placer cette fonction de gestion de l'événement dans un service". Il me semble que la réponse est la suivante. Dans la mesure où l'événement en question aura pour conséquence une communication avec un serveur, il est logique de la placer dans un service.
Création du formulaire
On continue par ajouter les fonctions de formulaire à la page de détails.
Dans app/details/details.component.ts, ajouter l'import suivant :
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
Dans le décorateur de DetailsComponent, mettons à jour la propriété imports :
imports: [ CommonModule, ReactiveFormsModule ],
Dans la class DetailsComponent, avant le constructeur, on ajoute le code suivant pour créer l'objet formulaire :
applyForm = new FormGroup({ firstName: new FormControl(''), lastName: new FormControl(''), email: new FormControl('') });
FormGroup et FormControl sont des types qui permettent de créer des formulaires. Le type FormControl peut fournir une valeur par défaut et définir les données du formulaire. Dans cet exemple, firstName, lastName et email sont des chaînes de caractères et leur valeur par défaut est une chaîne vide.
Appel du service qui gère la soumission du formulaire
Dans la class DetailsComponent, après le constructeur, on ajoute le code suivant pour gérer la soumission du formulaire :
submitApplication() { this.housingService.submitApplication( this.applyForm.value.firstName ?? '', this.applyForm.value.lastName ?? '', this.applyForm.value.email ?? '' ); }
Vous observez que ce code utilise l'opérateur de coalescence nul pour définir par défaut une chaîne vide si la valeur est nulle.
Ajout du formulaire dans le template
Toujour dans src/app/details/details.component.ts, modifier la propriété template avec le code suivant :
template: ` <article> <img class="listing-photo" [src]="housingLocation?.photo" alt="Exterior photo of {{housingLocation?.name}}"/> <section class="listing-description"> <h2 class="listing-heading">{{housingLocation?.name}}</h2> <p class="listing-location">{{housingLocation?.city}}, {{housingLocation?.state}}</p> </section> <section class="listing-features"> <h2 class="section-heading">About this housing location</h2> <ul> <li>Units available: {{housingLocation?.availableUnits}}</li> <li>Does this location have wifi: {{housingLocation?.wifi}}</li> <li>Does this location have laundry: {{housingLocation?.laundry}}</li> </ul> </section> <section class="listing-apply"> <h2 class="section-heading">Apply now to live here</h2> <form [formGroup]="applyForm" (submit)="submitApplication()"> <label for="first-name">First Name</label> <input id="first-name" type="text" formControlName="firstName"> <label for="last-name">Last Name</label> <input id="last-name" type="text" formControlName="lastName"> <label for="email">Email</label> <input id="email" type="email" formControlName="email"> <button type="submit" class="primary">Apply now</button> </form> </section> </article> `,
Vous noterez au passage la syntaxe de liaison d'événement Angular. Elle est composée d'un nom d'événement cible entre parenthèses à gauche d'un signe égal et d'une instruction de modèle entre guillemets à droite. Ce qui donne :
(submit)="submitApplication()"
Mise en page CSS
Pour améliorer la mise en page, collez le code suivant dans le fichier details.component.css :
.listing-photo { height: 600px; width: 50%; object-fit: cover; border-radius: 30px; float: right; } .listing-heading { font-size: 48pt; font-weight: bold; margin-bottom: 15px; } .listing-location::before { content: url('/assets/location-pin.svg') / ''; } .listing-location { font-size: 24pt; margin-bottom: 15px; } .listing-features > .section-heading { color: var(--secondary-color); font-size: 24pt; margin-bottom: 15px; } .listing-features { margin-bottom: 20px; } .listing-features li { font-size: 14pt; } li { list-style-type: none; } .listing-apply .section-heading { font-size: 18pt; margin-bottom: 15px; } label, input { display: block; } label { color: var(--secondary-color); font-weight: bold; text-transform: uppercase; font-size: 12pt; } input { font-size: 16pt; margin-bottom: 15px; padding: 10px; width: 400px; border-top: none; border-right: none; border-left: none; border-bottom: solid .3px; } @media (max-width: 1024px) { .listing-photo { width: 100%; height: 400px; } }
Résultats
Si vous ouvrez le détail d'une location, vous pouvez maintenant renseigner le formulaire. Si vous le validez en ayant préalablement ouvert la console de votre navigateur, vous verrez que les valeurs entrées sont bien récupérées et affichées.