Dynamisez votre HTML
Angular apporte une syntaxe puissante aux templates. Dans le chapitre précédent, nous avons vu l'interpolation de texte {{}}
. Dans ce chapitre, nous aborderons quelques éléments de ce système de syntaxe : le property binding, l'event binding, le class et style binding, les directives d'attribut et les directives structurelles.
Property binding
Pour lier un élément HTML à la propriété d'un composant, placez-le entre crochets []
. Les crochets, []
, obligent Angular à évaluer le côté droit de l'affectation en tant qu'expression dynamique. Sans les crochets, Angular traite le côté droit comme un littéral et donne pour valeur à la propriété cette valeur statique. []
est la syntaxe pour le one-way data binding avec les données circulant du composant vers le template.
<a [href]="url">Link</a>
<button [disabled]="isUnchanged">Disabled Button</button>
Exercice: Essayez de lier les attributs src
et width
de l'image
Class et style binding
Class binding
Vous pouvez utiliser le class binding pour ajouter et supprimer des noms de classe CSS à l'attribut class
d'un élément. Pour créer un class binding pour une seule classe, utilisez le préfixe class
suivi d'un point et du nom de la classe CSS, par exemple, [class.sale]="onSale"
. Angular ajoute la classe lorsque l'expression liée, onSale
, est vraie et supprime la classe lorsque l'expression est fausse.
<p [class.my-class-1]="isWarning"></p>
Plusieurs classes peuvent également être liées avec la syntaxe [class]
:
<!-- classExpression = "my-class-1 my-class-2 my-class-3" -->
<!-- classExpression = {my-class-1: true, my-class-2: false} -->
<!-- classExpression = ['my-class-1', 'my-class-2'] -->
<p [class]="classExpression"></p>
Style binding
Vous pouvez utiliser le style binding pour définir des styles de manière dynamique. Pour créer un style binding pour une seule propriété de style CSS, utilisez le préfixe style
suivi d'un point et du nom de la propriété de style CSS, par exemple, [style.width]="width" avec width = "100px"
(width est un chaîne de caractères). En option, vous pouvez ajouter une extension d'unité comme em
ou %
: [style.width.px]="width" avec width = 100
(width est un nombre).
<!-- Style properties can be written in dash-case or camelCase -->
<nav [style.background-color]="expression"></nav>
<nav [style.backgroundColor]="expression"></nav>
Pour basculer entre plusieurs styles, lier à l'attribut [style]
:
<!-- styleExpression = "width: 100px; height: 100px; background-color: red;" -->
<!-- styleExpression = {width: '100px', height: '100px', backgroundColor: 'red'} -->
<p [style]="styleExpression"></p>
Directives NgClass et NgStyle
Alternativement, vous pouvez dynamiquement activer plusieurs styles ou plusieurs classes à la fois via deux directives NgStyle et NgClass :
<!-- toggle the "special" class on/off with a property -->
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<!-- toggle the "saveable" class on/off depending on canSave -->
<!-- toggle the "modified" class on/off depending on isUnchanged -->
<!-- toggle the "special" class on/off depending on isSpecial -->
<div [ngClass]="{saveable: canSave, modified: !isUnchanged, special: isSpecial}">
NgClass test div
</div>
<div [ngStyle]="{
'font-style': canSave ? 'italic' : 'normal',
'font-weight': !isUnchanged ? 'bold' : 'normal',
'font-size': isSpecial ? '24px' : '12px'
}">
NgStyle test div
</div>
Exercise: attribuer une classe et une couleur à chaque fantôme en utilisant [class], [style], [ngClass] ou [ngStyle]
Directive NgModel
La directive NgModel vous permet de lier la valeur d'un champ de formulaire à une propriété du composant. Il s'agit d'une liaison bidirectionnelle : la propriété est mise à jour lorsque le contenu du champ change (typiquement par l'utilisateur) et vice versa. La syntaxe pour la liaison de données bidirectionnelle est [()]
.
<label>
What is your name ?
<input [(ngModel)]="name">
</label>
<p>Hello {{ name }} !</p>
Testez-le vous-même :
Hello !
Import
La directive NgModel
ne fait pas partie des imports par défaut d'un NgModule
. Vous devez l'ajouter vous-même : ajoutez FormsModule
à la liste d'imports de l'AppModule
.
Exercice : utilisez [(ngModel)] sur les balises input, select, radio et checkbox
Directive NgIf
Vous pouvez ajouter ou supprimer un élément en appliquant une directive NgIf
à un élément hôte. Lorsque NgIf
est faux, Angular supprime un élément et ses descendants du DOM. Angular supprime ensuite les instances des classes de ces composants s'il en existe, ce qui libère de la mémoire et des ressources. Si vous souhaitez uniquement masquer l'élément, vous pouvez utiliser [hidden]
qui ajoute/supprime uniquement la propriété CSS display:none
sur l'élément. NgIf
est utile pour fournir un moyen de se prémunir contre les valeurs nulles.
<!--Will only show Hello, ... if currentCustomer is not null or undefined-->
<div *ngIf="currentCustomer">Hello, {{currentCustomer.name}}</div>
WARNING
Soyez prudent lorsque vous utilisez NgIf
pour tester la nullité sur des valeurs numériques car 0
est une valeur fausse.
Vous pouvez fournir une instruction else comme suit :
<div *ngIf="condition; else elseBlock">Content to render when condition is true.</div>
<ng-template #elseBlock>Content to render when condition is false.</ng-template>
<ng-template>
crée un fragment de template, il n'est pas rendu par défaut. #elseBlock
est une variable de template qui permet d'obtenir une référence au <ng-template>
.
Exercice: utilisez un *ngIf
pour montrer/cacher le loader
Bonus: utilisez un *ngIf else
pour conditionnellement montrer soit les données soit le message comme quoi les données ne sont pas disponibles
Directive NgSwitch
NgSwitch
échange conditionnellement le contenu de l'élément hôte en sélectionnant l'un des template enfants en fonction de la valeur actuelle de l'expression.
<div [ngSwitch]="myBeer">
<div *ngSwitchCase="'Ale'">Short fermentation</div>
<div *ngSwitchCase="'Lager'">Long fermentation</div>
<div *ngSwitchCase="'Sour ale'">Crafted from wild yeasts</div>
<div *ngSwitchDefault>No random knowledge for that type of beer, sorry.</div>
</div>
Exercice : utilisez *ngSwitch
pour alterner le stade de croissance des plantes en fonction de la température de la saison
Directive NgFor
Vous pouvez utiliser la directive NgFor pour présenter une liste d'éléments. L'élément sur lequel NgFor
est placé sera répété pour chaque élément de l'itérable.
<div *ngFor="let item of items">{{item.name}}</div>
<!-- With a local variable for the index -->
<div *ngFor="let item of items; let i = index">{{i}}: {{item.name}}</div>
<!-- With a local variable to know whether it is an even item -->
<div *ngFor="let item of items; let isEven = even">
{{item.name}} is {{isEven ? 'even': 'odd'}}
</div>
Les valeurs exportées suivantes sont également disponibles pour être associées à des variables locales : count
, first
, last
, odd
.
*ngIf
et *ngFor
ne peuvent pas être placés en même temps sur un élément HTML. Pour répéter un bloc d'HTML lorsqu'une condition particulière est vraie, soit un niveau supplémentaire d'HTML doit être introduit, ce qui n'est pas toujours souhaitable et peut casser le style, soit la balise <ng-container>
fournie par Angular peut être utilisée . <ng-container>
n'est pas présent dans le DOM.
<!-- Without ng-container -->
<div *ngIf="condition">
<div *ngFor="let item of items">{{item.name}}</div>
</div>
<!-- With ng-container -->
<ng-container *ngIf="condition">
<div *ngFor="let item of items">{{item.name}}</div>
</ng-container>
Exercice : utilisez deux boucles *ngFor
pour afficher tout le contenu du panier (une boucle pour chaque type d'article, et dans cette boucle une autre boucle pour imprimer autant d'emoji de cet article que sa quantité)
Bonus : Un intrus est dans la corbeille de fruits, cachez le maïs avec un *ngIf
Event binding
L'event binding permet d'écouter et de répondre aux actions de l'utilisateur telles que les frappes, les mouvements de souris, les clics et les touchers ou un événement custom émis par un composant enfant. Pour lier le composant à un événement, utilisez la syntaxe d'event binding d'Angular ()
.
<button (click)="delete()">Delete</button>
Exercice : utilisez des événements pour ajouter un singe lorsque vous cliquez sur le bouton et faites-lui ouvrir les yeux au survol de la souris
À propos des directives
Dans ce chapitre, nous avons vu 5 directives intégrées. Les directives sont des classes déclarées avec le décorateur @Directive
.
Il existe trois types de directives :
- Les composants qui sont des directives avec un template (
@Component
hérite de@Directive
) - Les directives d'attributs qui modifient l'apparence ou le comportement d'un élément
- Directive structurelle qui modifie la mise en page DOM en ajoutant et en supprimant des éléments DOM
Quiz : Quelles directives fournies par Angular sont des directives d'attribut et lesquelles sont des directives structurelles ?
Vous pouvez en savoir plus sur la création de vos propres directives ici et ici.
TP : Liste des films
- Dans le composant LoginFormComponent, ajoutez deux champs
email
etpassword
et utilisez la directive[(ngModel)]
sur les inputs email et mot de passe pour les lier. Rappelez-vous de l'avertissement dans le paragraphe sur le NgModel : n'oubliez pas d'importer leFormsModule
dans le module pour utiliser la directive NgModel. - Ajoutez un autre champ
loggedIn
initialement défini àfalse
, puis utilisez l'event binding avec(ngSubmit)
sur la baliseform
pour le passer àtrue
lorsque le formulaire est soumis (créez une méthodelogin()
dans la classe du composant pour ça). - Dans
login-form.component.html
, ajoutez le code HTML suivant sous le formulaire d'authentification :
<ul class="films">
<li class="film card">
<img
class="poster"
src="https://m.media-amazon.com/images/M/MV5BMDdmZGU3NDQtY2E5My00ZTliLWIzOTUtMTY4ZGI1YjdiNjk3XkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg"
/>
<div>
<p class="title">
Titanic
<span class="rating">★★★★</span>
</p>
<dl>
<dt>Release date</dt>
<dd>07/01/1998</dd>
<dt>Director</dt>
<dd>James Cameron</dd>
<dt>Actors</dt>
<dd>Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates</dd>
</dl>
<p class="plot">
84 years later, a 100 year-old woman named Rose DeWitt Bukater tells the
story to her granddaughter Lizzy Calvert, Brock Lovett, Lewis Bodine,
Bobby Buell and Anatoly Mikailavich on the Keldysh about her life set in
April 10th 1912, on a ship called Titanic when young Rose boards the
departing ship with the upper-class passengers and her mother, Ruth DeWitt
Bukater, and her fiancé, Caledon Hockley. Meanwhile, a drifter and artist
named Jack Dawson and his best friend Fabrizio De Rossi win third-class
tickets to the ship in a game. And she explains the whole story from
departure until the death of Titanic on its first and last voyage April
15th, 1912 at 2:20 in the morning.
</p>
</div>
</li>
</ul>
- Utilisez la directive
*ngIf else
pour afficher le formulaire d'authentification et masquer la liste des films lorsqueloggedIn === false
, et vice versa. - Ajoutez le modèle suivant dans le dossier src/app/models, nommez le fichier film.ts :
export interface Film {
title: string
released: string
director: string
actors: string
poster: string
plot: string
metascore: string
}
- Ajoutez le champ suivant dans la classe du composant LoginFormComponent (VSCode devrait vous affichez un erreur et vous proposez d'importer le modèle Film dans le composant, vous devriez voir l'alias @models utilisé dans l'import) :
films: Film[] = [
{
title: 'Titanic',
released: '19 Dec 1997',
director: 'James Cameron',
actors: 'Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates',
poster: 'https://m.media-amazon.com/images/M/MV5BMDdmZGU3NDQtY2E5My00ZTliLWIzOTUtMTY4ZGI1YjdiNjk3XkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg',
plot: `84 years later, a 100 year-old woman named Rose DeWitt Bukater tells the story to her granddaughter Lizzy Calvert, Brock Lovett, Lewis Bodine, Bobby Buell and Anatoly Mikailavich on the Keldysh about
her life set in April 10th 1912, on a ship called Titanic when young Rose boards the departing ship with the upper-class passengers and her mother, Ruth DeWitt Bukater, and her fiancé, Caledon Hockley.
Meanwhile, a drifter and artist named Jack Dawson and his best friend Fabrizio De Rossi win third-class tickets to the ship in a game. And she explains the whole story from departure until the death of Titanic
on its first and last voyage April 15th, 1912 at 2:20 in the morning.`,
metascore: '75'
},
{
title: 'Blade Runner',
released: '25 Jun 1982',
director: 'Ridley Scott',
actors: 'Harrison Ford, Rutger Hauer, Sean Young, Edward James Olmos',
poster: 'https://m.media-amazon.com/images/M/MV5BNzQzMzJhZTEtOWM4NS00MTdhLTg0YjgtMjM4MDRkZjUwZDBlXkEyXkFqcGdeQXVyNjU0OTQ0OTY@._V1_SX300.jpg',
plot: 'A blade runner must pursue and terminate four replicants who stole a ship in space, and have returned to Earth to find their creator.',
metascore: '89'
},
{
title: 'The Shining',
released: '13 Jun 1980',
director: 'Stanley Kubrick',
actors: 'Jack Nicholson, Shelley Duvall, Danny Lloyd, Scatman Crothers',
poster: 'https://m.media-amazon.com/images/M/MV5BZWFlYmY2MGEtZjVkYS00YzU4LTg0YjQtYzY1ZGE3NTA5NGQxXkEyXkFqcGdeQXVyMTQxNzMzNDI@._V1_SX300.jpg',
plot: 'A family heads to an isolated hotel for the winter where an evil spiritual presence influences the father into violence, while his psychic son sees horrific forebodings from both past and future.',
metascore: '63'
}
]
- En utilisant la directive
*ngFor
, répétez l'élément.film.card
pour afficher autant de films qu'il y en a dans la listefilms
. A ce stade, le Titanic est affiché trois fois, occupons-nous de ça dans l'étape suivante. - Complétez la carte avec les données de chaque film en utilisant le property binding et l'interpolation.
- Bonus : Utilisez la propriété
metascore
pour afficher un nombre d'étoiles (de 1 à 5 ★) à côté de chaque titre de film (créez une méthodestarRating
retournant une string comportant le bon nombre d'étoiles à cet effet). - Bonus : Utilisez une balise
ng-container
pour afficher uniquement les films avec un score métacritique supérieur à 70 (dans la suite du TP, nous n'aurons pas besoin de ce changement, ne le conservez pas une fois que vous êtes arrivé à le faire marcher). - N'oubliez pas de commiter.