Documentation
Modules de fonctionnalité
Créer un module Angular pour chaque fonctionnalité distincte de l'application Placer les modules de fonctionnalité dans des répertoires correspondant à leur zone (ex: app/heroes) Nommer les fichiers des modules selon leur zone et leur répertoire (ex: app/heroes/heroes.module.ts)
Module de fonctionnalité partagé
Créer un SharedModule dans le répertoire shared (ex: app/shared/shared.module.ts) Déclarer dans SharedModule les composants, directives et pipes réutilisables Importer dans SharedModule les dépendances courantes (ex: CommonModule, FormsModule)
Module de fonctionnalité cantral
Regrouper les classes utilitaires à usage unique dans un CoreModule Créer un CoreModule dans le répertoire core (ex: app/core/core.module.ts) Ajouter au CoreModule les services singleton utilisés globalement (ex: ExceptionService, LoggerService) Les composants utilisés une seule fois (ex: animations de chargement) ne doivent pas être importés ailleurs qu'en AppComponent
Services
Les composants doivent se concentrer sur l'interface utilisateur Déléguer les tâches métier (ex: requêtes HTTP, validation) à des services injectables
- Expressions d'interpolation
- Boucles
- Liaisons événementielles et propriétés
- Liaison bidirectionnelle
- Conditions
- ngSwitch
- Variables de template
- Pipes
- Directives d'attribut
- Directives structurelles
- ViewChild
- Liaison de classes
- Liaison de styles
- Configuration du proxy
- ng-container
Expressions d'interpolation
<div>{{ titre }}</div>
<div>{{ true ? 1 : 2 }}</div>
<div>{{ getTitre() }}</div>
<div>{{ null ?? 'x' }}</div>
<div>{{ hero?.nom }}</div>
<div>{{ titres?.[0] }}</div>
<!-- Concaténation -->
<li *ngFor="let item of arr" title="{{item}}.txt">{{item}}</li>
<li *ngFor="let item of arr" [title]="item + '.txt'">{{item}}</li>
<!-- Injection HTML -->
<div [innerHTML]="contenu"></div>
contenu = `<h2>Titre</h2>`
Boucles
La fonction trackBy retourne un identifiant unique
<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>
trackById(index, item) {
return item.id;
}
couleurs = ['rouge', 'bleu', 'vert'];
- item = {{ item }},
index = {{ i }},
Premier? = {{ f }},
Dernier? = {{ l }},
Pair? = {{ e }},
Impair? = {{ o }}
Génération de nombres
@Pipe({name: 'nombre'})
export class NombrePipe implements PipeTransform {
transform(valeur: number): any {
return Array.from({length: valeur}, (_, i) => i);
}
}
- Méthode 1 avec PIPE
- {{n}}
Approche statique
- Méthode 2
- {{n}}
Flux de données
- {{item}} {{ data.length }}
export class AppComponent {
liste = new BehaviorSubject<string[]>(["a", "b", "c"]);
}
Liaisons événementielles et propriétés
<button (click)="alerter()" [title]="titre">Cliquez</button>
Liaison bidirectionnelle
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [FormsModule] // Généralement exporté par SharedModule
})
<input type="text" name="nom" [(ngModel)]="nom" /> {{ nom }}
Événement personnalisé
<input type="text" name="nom" [(ngModel)]="nom" (ngModelChange)="modifierNom($event)"/> {{ nom }}
nom = "utilisateur";
modifierNom(v: string) {
this.nom = v.toLocaleUpperCase();
}
Conditions
monNom = '';
ngOnInit() {
setTimeout(() => this.monNom = 'seul', 2000);
}
<p *ngIf="monNom; else tempSinon">{{ monNom }}</p>
<ng-template #tempSinon>
<p>Données indisponibles !</p>
</ng-template>
Conditions if/then/else
<p *ngIf="monNom; then tempSi; else tempSinon"></p>
<ng-template #tempSi>
<p>{{ monNom }}</p>
</ng-template>
<ng-template #tempSinon>
<p>Données indisponibles !</p>
</ng-template>
ngSwitch
<div [ngSwitch]="utilisateur">
<p *ngSwitchCase="'utilisateur1'">Bonjour {{utilisateur}}</p>
<p *ngSwitchCase="'utilisateur2'">Au revoir {{utilisateur}}</p>
<p *ngSwitchDefault>Aucun utilisateur !</p>
</div>
Variables de template
<div>{{ champ.nom }}_{{ champ.valeur }}</div>
<input type="text" name="champ" value="123" #champ />
<button (click)="traiter(champ.valeur)">Cliquez</button>
traiter(v: string) {
console.log(v); // 123
}
<!-- Accès à l'instance du composant -->
<app-bonjour #bonjour></app-bonjour>
<button (click)="bonjour.evenement()">Cliquez</button>
Pipes
<p>{{ 'utilisateur' | uppercase }}</p> MAJ -> UTILISATEUR
<p>{{ 'Utilisateur' | lowercase }}</p> minuscule -> utilisateur
<p>{{ 'salut utilisateur' | titlecase }}</p> Majuscule par mot -> Salut Utilisateur
<p>{{ {nom: 'utilisateur'} | json }}</p> Objet => JSON
<p>{{ 'utilisateur' | slice:1:3 }}</p> Sous-chaîne -> ut
Traitement des nombres
<p>{{ 3.141 | number:'1.2-2' }}</p> -> 3,14
<p>{{ 0.5 | percent }}</p> Pourcentage -> 50%
<p>Devise : {{ 5 | currency }}</p>
<p>GBP : {{ 5 | currency:'GBP' }}</p>
Pipes personnalisés
- Création
ng g @schematics/angular:pipe arrondi --project=projet --module=app.module.ts --no-spec
- Utilisation
<div>{{ 12.3 | arrondi: 2 }}</div>
- Implémentation
import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
name: "arrondi",
})
export class ArrondiPipe implements PipeTransform {
transform(valeur: number, multiplicateur?: number): any {
return Math.ceil(valeur * multiplicateur) / multiplicateur;
}
}
Communication entre composants
<app-bonjour [nom]="nom" (alerte)="traiterAlerte($event)"></app-bonjour>
nom = "utilisateur";
traiterAlerte(v: string) {
console.log(v);
}
<h2 (click)="gérerClic()">{{nom}}</h2>
@Input() nom: string;
@Output() alerte = new EventEmitter<string>();
gérerClic() {
this.alerte.emit("Bonjour...");
}
Directives d'attribut
- Création
ng g directive miseEnRelief
- Utilisation
<h1 [appMiseEnRelief]="'bleu'" [couleurParDéfaut]="'#f48'">Bienvenue {{ titre }} !</h1>
- Implémentation
import { Directive, ElementRef, HostListener, Input } from "@angular/core";
@Directive({
selector: "[appMiseEnRelief]",
})
export class MiseEnReliefDirective {
@Input("appMiseEnRelief")
couleur: string;
@Input()
set couleurParDéfaut(c: string) {
this.changerCouleur(c);
}
constructor(private el: ElementRef) {}
@HostListener("mouseenter")
entréeSouris(): void {
this.changerCouleur(this.couleur || "red");
}
@HostListener("mouseleave")
sortieSouris(): void {
this.changerCouleur(null);
}
private changerCouleur(c: string): void {
this.el.nativeElement.style.color = c;
}
}
Directives structurelles
- Création
ng g @schematics/angular:directive délai --project=projet --module=app.module.ts --no-spec
- Utilisation
<div *appDélai="1200">
Bonjour <ng-container *ngIf="!!nomUtilisateur"> {{ nomUtilisateur }} </ng-container>
</div>
- Implémentation
import {
Directive,
Input,
TemplateRef,
ViewContainerRef,
OnDestroy,
} from "@angular/core";
@Directive({
selector: "[appDélai]",
})
export class DélaiDirective implements OnDestroy {
private minuteur: NodeJS.Timer;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
) {}
@Input()
public set appDélai(durée: number) {
this.minuteur = setTimeout(() => {
this.viewContainer.createEmbeddedView(this.templateRef);
}, durée);
}
ngOnDestroy(): void {
clearTimeout(this.minuteur);
}
}
ViewChild
Accès à l'instance d'un composant enfant ou au DOM
<app-bonjour></app-bonjour>
<button (click)="cliquer()">Cliquez</button>
import { Component, AfterViewInit, ViewChild } from "@angular/core";
import { BonjourComponent } from "./bonjour/bonjour.component";
@Component({...})
export class AppComponent implements AfterViewInit {
@ViewChild(BonjourComponent)
bonjourRef: BonjourComponent;
ngAfterViewInit() {
this.bonjourRef.traiter();
}
cliquer() {
this.bonjourRef.traiter();
}
}
Accès au DOM
<div #test>bonjour</div>
@ViewChild("test") référenceDOM: ElementRef;
ngAfterViewInit(): void {
console.log(this.référenceDOM.nativeElement.textContent); // bonjour
}
Opérateurs d'assurance de type
Le nom actuel est {{héros?.nom}} // Sécurisé
<div *ngIf="héros">
Le nom est {{héros!.nom}} // Assertion non nulle
</div>
Membre non déclaré est {{$any(this).membre}} // Conversion en any
Liaison de classes
classes = {
'text-danger': true,
'text-success': false,
'p-2': true
};
<p class="a" [class]="'text-danger'">{{ texte() }}</p> // 'text-danger'
<p class="a" [class.text-danger]="true">{{ texte() }}</p> // 'a text-danger'
<p class="a" [ngClass]="classes">{{ texte() }}</p> // 'a text-danger p-2'
Liaison de styles
styles = {
couleur: 'rouge',
padding: '1rem',
fond: '#e5e5ef75',
affichage: 'inline-block'
};
<p style="font-family: Consolas;" [style.couleur]="'rouge'">{{ texte() }}</p>
<p style="font-family: Consolas;" [ngStyle]="styles">{{ texte() }}</p>
Configuration du proxy
- Documentation 2
proxy.conf.json
{
"/api": {
"target": "http://localhost:5000",
"secure": false,
"pathRewrite": {
"^/api": ""
}
}
}
ngOnInit() {
this.message$ = this.http.get("/api/ng7", {
responseType: "text",
});
}
ng-container
Pour attacher des directives structurelles sans balise HTML supplémnetaire
<div>
Bonjour <ng-container *ngIf="!!nomUtilisateur"> {{ nomUtilisateur }} </ng-container>
</div>
<div>
Bonjour <span *ngIf="!!nomUtilisateur"> {{ nomUtilisateur }} </span>
</div>
Événements clavier
<input #zone (keyup.enter)="entrer(zone.value); zone.value=''">