Directives
Types de Directives
Section intitulée « Types de Directives »- Structurelles : Modifient la structure du DOM (
*ngIf,*ngFor,*ngSwitch) - D’attribut : Modifient l’apparence/comportement d’un élément (
ngClass,ngStyle) - Personnalisées : Créez vos propres directives réutilisables
*ngIf - Rendu Conditionnel
Section intitulée « *ngIf - Rendu Conditionnel »Utilisation basique :
import { Component } from '@angular/core';import { CommonModule } from '@angular/common';
@Component({ selector: 'app-player-status', standalone: true, imports: [CommonModule], template: ` <div *ngIf="isPlaying"> <p>🎵 Now Playing...</p> </div>
<button (click)="isPlaying = !isPlaying">Toggle Playback</button> `})export class PlayerStatusComponent { isPlaying = false;}Avec else :
<div *ngIf="album; else loading"> <h2>{{ album.name }}</h2> <p>{{ album.artist }}</p></div>
<ng-template #loading> <p>Loading album data...</p></ng-template>Avec then/else :
<div *ngIf="isPremium; then premiumContent else freeContent"></div>
<ng-template #premiumContent> <p>🎧 Premium Subscription - Unlimited Downloads</p></ng-template>
<ng-template #freeContent> <p>🎵 Free Tier - Stream Only</p></ng-template>Stocker le résultat (as) :
<div *ngIf="album$ | async as album"> <p>{{ album.name }}</p> <p>{{ album.artist }}</p></div>*ngFor - Rendu de Liste
Section intitulée « *ngFor - Rendu de Liste »Utilisation basique :
@Component({ selector: 'app-track-list', standalone: true, imports: [CommonModule], template: ` <ul> <li *ngFor="let track of tracks"> {{ track.title }} </li> </ul> `})export class TrackListComponent { tracks = [ { id: 1, title: 'Bohemian Rhapsody' }, { id: 2, title: 'Stairway to Heaven' }, { id: 3, title: 'Hotel California' } ];}Avec index :
<li *ngFor="let track of tracks; let i = index"> {{ i + 1 }}. {{ track.title }}</li>Avec trackBy pour les performances :
@Component({ selector: 'app-album-list', standalone: true, imports: [CommonModule], template: ` <div *ngFor="let album of albums; trackBy: trackByAlbumId"> {{ album.name }} </div> `})export class AlbumListComponent { albums = [ { id: 1, name: 'Dark Side of the Moon', artist: 'Pink Floyd' }, { id: 2, name: 'Abbey Road', artist: 'The Beatles' } ];
// Suit les éléments par ID, évite les re-rendus inutiles trackByAlbumId(index: number, album: any): number { return album.id; }}*Toutes les variables ngFor :
<li *ngFor="let track of tracks; let i = index; let first = first; let last = last; let even = even; let odd = odd"> Track {{ i }}: {{ track }} <span *ngIf="first">(Opening Track)</span> <span *ngIf="last">(Closing Track)</span></li>*ngSwitch - Conditions Multiples
Section intitulée « *ngSwitch - Conditions Multiples »@Component({ selector: 'app-player-status', standalone: true, imports: [CommonModule], template: ` <div [ngSwitch]="status"> <p *ngSwitchCase="'playing'" class="text-green">▶️ Playing</p> <p *ngSwitchCase="'paused'" class="text-yellow">⏸️ Paused</p> <p *ngSwitchCase="'stopped'" class="text-gray">⏹️ Stopped</p> <p *ngSwitchDefault class="text-red">Unknown Status</p> </div>
<button (click)="changeStatus()">Change Player Status</button> `})export class PlayerStatusComponent { status: string = 'playing';
changeStatus() { const statuses = ['playing', 'paused', 'stopped', 'unknown']; const current = statuses.indexOf(this.status); this.status = statuses[(current + 1) % statuses.length]; }}ngClass - Classes CSS Dynamiques
Section intitulée « ngClass - Classes CSS Dynamiques »Syntaxe objet :
@Component({ selector: 'app-play-button', standalone: true, imports: [CommonModule], template: ` <button [ngClass]="{ 'btn-playing': isPlaying, 'btn-large': isLarge, 'btn-disabled': isDisabled }"> ▶️ Play Track </button> `, styles: [` .btn-playing { background: green; color: white; } .btn-large { font-size: 18px; padding: 12px; } .btn-disabled { opacity: 0.5; cursor: not-allowed; } `]})export class PlayButtonComponent { isPlaying = true; isLarge = false; isDisabled = false;}Syntaxe tableau :
<div [ngClass]="['class1', 'class2', 'class3']"> Content</div>Syntaxe chaîne :
<div [ngClass]="'class1 class2'"> Content</div>Syntaxe méthode :
@Component({ template: ` <div [ngClass]="getClasses()">Content</div> `})export class MyComponent { getClasses() { return { 'active': this.isActive, 'highlighted': this.isHighlighted }; }}ngStyle - Styles Inline Dynamiques
Section intitulée « ngStyle - Styles Inline Dynamiques »Syntaxe objet :
@Component({ selector: 'app-track-title', standalone: true, imports: [CommonModule], template: ` <p [ngStyle]="{ 'color': textColor, 'font-size': fontSize + 'px', 'font-weight': isBold ? 'bold' : 'normal', 'background-color': bgColor }"> 🎵 Now Playing: Bohemian Rhapsody </p>
<input [(ngModel)]="textColor" placeholder="Color"> <input type="number" [(ngModel)]="fontSize"> `})export class TrackTitleComponent { textColor = 'blue'; fontSize = 16; isBold = false; bgColor = '#f0f0f0';}Directive d’Attribut Personnalisée
Section intitulée « Directive d’Attribut Personnalisée »Directive moderne basée sur les signaux :
import { Directive, ElementRef, input, effect, inject } from '@angular/core';
@Directive({ selector: '[appHighlight]', standalone: true, host: { '(mouseenter)': 'onMouseEnter()', '(mouseleave)': 'onMouseLeave()' }})export class HighlightDirective { private el = inject(ElementRef);
// Entrées basées sur les signaux appHighlight = input<string>('yellow'); defaultColor = input<string>('transparent');
constructor() { // Définit la couleur par défaut à l'initialisation effect(() => { this.el.nativeElement.style.backgroundColor = this.defaultColor(); }); }
onMouseEnter() { this.highlight(this.appHighlight()); }
onMouseLeave() { this.highlight(this.defaultColor()); }
private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}Utilisation dans un composant :
@Component({ selector: 'app-playlist', standalone: true, imports: [HighlightDirective], template: ` <p appHighlight>🎵 Track 1: Bohemian Rhapsody</p> <p [appHighlight]="'lightblue'">🎵 Track 2: Stairway to Heaven</p> <p [appHighlight]="'pink'" [defaultColor]="'lightgray'"> 🎵 Track 3: Hotel California </p> `})export class PlaylistComponent {}Directive Structurelle Personnalisée
Section intitulée « Directive Structurelle Personnalisée »*Directive Unless (inverse de ngIf) :
import { Directive, input, effect, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]', standalone: true})export class UnlessDirective { // Entrée basée sur les signaux appUnless = input<boolean>(false);
constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef ) { // L'effet réagit aux changements de condition effect(() => { const condition = this.appUnless();
if (!condition) { // Affiche le contenu quand la condition est FALSE if (this.viewContainer.length === 0) { this.viewContainer.createEmbeddedView(this.templateRef); } } else { // Supprime le contenu quand la condition est TRUE this.viewContainer.clear(); } }); }}Utilisation :
@Component({ selector: 'app-track-display', standalone: true, imports: [UnlessDirective], template: ` <p *appUnless="isHidden"> 🎵 Now Playing: Bohemian Rhapsody - Queen </p> <button (click)="isHidden = !isHidden">Toggle Track Display</button> `})export class TrackDisplayComponent { isHidden = false;}Directives Personnalisées Pratiques
Section intitulée « Directives Personnalisées Pratiques »Directive Click Outside
Section intitulée « Directive Click Outside »import { Directive, ElementRef, output, inject } from '@angular/core';
@Directive({ selector: '[appClickOutside]', standalone: true, host: { '(document:click)': 'onClick($event.target)' }})export class ClickOutsideDirective { private elementRef = inject(ElementRef); // Sortie basée sur les signaux clickOutside = output<void>();
onClick(target: HTMLElement) { const clickedInside = this.elementRef.nativeElement.contains(target); if (!clickedInside) { this.clickOutside.emit(); } }}Utilisation :
@Component({ template: ` <div class="genre-filter" appClickOutside (clickOutside)="closeFilter()"> <button (click)="isOpen = !isOpen">🎸 Genre Filter</button> <div *ngIf="isOpen" class="menu"> <p>🎸 Rock</p> <p>🎹 Jazz</p> <p>🎤 Pop</p> </div> </div> `})export class GenreFilterComponent { isOpen = false;
closeFilter() { this.isOpen = false; }}Directive Auto-focus
Section intitulée « Directive Auto-focus »import { Directive, ElementRef, OnInit, inject } from '@angular/core';
@Directive({ selector: '[appAutoFocus]', standalone: true})export class AutoFocusDirective implements OnInit { private el = inject(ElementRef);
ngOnInit() { setTimeout(() => { this.el.nativeElement.focus(); }, 0); }}Utilisation :
<input appAutoFocus placeholder="Search for tracks...">Directive Debounce Click
Section intitulée « Directive Debounce Click »import { Directive, input, output } from '@angular/core';
@Directive({ selector: '[appDebounceClick]', standalone: true, host: { '(click)': 'onClick($event)' }})export class DebounceClickDirective { // Entrées/sorties basées sur les signaux debounceTime = input<number>(500); debounceClick = output<Event>();
private timeout: any;
onClick(event: Event) { event.preventDefault(); event.stopPropagation();
clearTimeout(this.timeout); this.timeout = setTimeout(() => { this.debounceClick.emit(event); }, this.debounceTime()); }}Utilisation :
<button appDebounceClick [debounceTime]="1000" (debounceClick)="addToFavorites()"> ⭐ Add to Favorites (debounced)</button>Bonnes Pratiques
Section intitulée « Bonnes Pratiques »- Utiliser les directives structurelles pour la manipulation du DOM
- Utiliser les directives d’attribut pour le style/comportement
- Préférer les directives intégrées plutôt que personnalisées quand c’est possible
- Utiliser trackBy avec *ngFor pour les performances
- Garder les directives personnalisées focalisées (responsabilité unique)
- Utiliser HostListener pour la gestion des événements
- Utiliser HostBinding pour la liaison de propriétés
- Rendre les directives personnalisées réutilisables et configurables
- Utiliser standalone: true pour toutes les nouvelles directives

