Bases d'Angular

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

  1. Création
ng g @schematics/angular:pipe arrondi --project=projet --module=app.module.ts --no-spec

  1. Utilisation
<div>{{ 12.3 | arrondi: 2 }}</div>

  1. 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

  1. Création
ng g directive miseEnRelief

  1. Utilisation
<h1 [appMiseEnRelief]="'bleu'" [couleurParDéfaut]="'#f48'">Bienvenue {{ titre }} !</h1>

  1. 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

  1. Création
ng g @schematics/angular:directive délai --project=projet --module=app.module.ts --no-spec

  1. Utilisation
<div *appDélai="1200">
  Bonjour <ng-container *ngIf="!!nomUtilisateur"> {{ nomUtilisateur }} </ng-container>
</div>

  1. 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=''">

Étiquettes: Angular modules composants templates directives

Publié le 16 septembre à 08h13