Installation & CLI
Angular CLI — commandes de base
npmCLI

Angular CLI est l'outil officiel pour créer, développer, tester et builder des applications Angular.

Bash
# Installer Angular CLI globalement
npm install -g @angular/cli

# Créer un nouveau projet
ng new mon-projet
ng new mon-projet --routing --style=css   # avec routing et CSS
cd mon-projet

# Lancer le serveur de développement
ng serve
ng serve --open                           # ouvre le navigateur
ng serve --port 4200

# Build
ng build
ng build --configuration production       # build de prod (optimisé)

# Tests
ng test                                   # tests unitaires (Karma)
ng e2e                                    # tests end-to-end

# Lint
ng lint
ng generate — Générer des fichiers
generate

La commande ng generate (alias ng g) permet de créer rapidement tous les types de fichiers Angular.

CommandeAliasDescription
ng generate component nomng g c nomCréer un composant
ng generate service nomng g s nomCréer un service
ng generate module nomng g m nomCréer un module
ng generate pipe nomng g p nomCréer un pipe
ng generate directive nomng g d nomCréer une directive
ng generate guard nomng g g nomCréer un guard
ng generate interface nomng g i nomCréer une interface
ng generate enum nomng g e nomCréer un enum
ng generate class nomng g cl nomCréer une classe
Bash
ng g c user/user-list         # dans un sous-dossier
ng g c user-card --inline-template --inline-style  # tout dans le .ts
ng g c nav --skip-tests       # sans spec.ts
ng g s auth --skip-tests
ng g m features/blog --routing  # module avec routing
Structure du projet
Arborescence d'un projet Angular
architecture

Un projet Angular suit une structure conventionnelle. Le dossier src/app/ contient toute la logique applicative.

Bash
mon-projet/
├── src/
│   ├── app/
│   │   ├── app.component.ts      # Composant racine
│   │   ├── app.component.html
│   │   ├── app.component.css
│   │   ├── app.module.ts         # Module principal
│   │   ├── app-routing.module.ts # Routes
│   │   ├── components/           # Composants réutilisables
│   │   ├── services/             # Services
│   │   ├── models/               # Interfaces/types
│   │   └── guards/               # Route guards
│   ├── assets/
│   ├── environments/
│   │   ├── environment.ts        # Dev
│   │   └── environment.prod.ts   # Prod
│   ├── index.html
│   └── styles.css                # Styles globaux
├── angular.json
├── tsconfig.json
└── package.json
Composants
Créer un composant avec @Input et @Output
@Component@Input/@Output

Un composant Angular est une classe TypeScript décorée avec @Component. @Input() reçoit des données du parent, @Output() émet des événements vers le parent.

TypeScript
// user-card.component.ts
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { User } from '../models/user.model';

@Component({
  selector: 'app-user-card',
  templateUrl: './user-card.component.html',
  styleUrls: ['./user-card.component.css'],
  // Standalone (Angular 14+)
  // standalone: true,
  // imports: [CommonModule],
})
export class UserCardComponent implements OnInit {
  @Input() user!: User;             // Données reçues du parent
  @Input() showActions = true;
  @Output() deleted = new EventEmitter<number>();  // Émet vers le parent

  titre: string = '';

  ngOnInit(): void {
    this.titre = `Profil de ${this.user.nom}`;
  }

  onDelete(): void {
    this.deleted.emit(this.user.id);
  }
}
HTML
<!-- user-card.component.html -->
<div class="card">
  <h2>{{ titre }}</h2>
  <p>{{ user.nom }} — {{ user.email }}</p>
  <button *ngIf="showActions" (click)="onDelete()">Supprimer</button>
</div>
TypeScript
// Interface User
// models/user.model.ts
export interface User {
  id: number;
  nom: string;
  email: string;
  role: 'admin' | 'user';
  actif: boolean;
}
Templates & Data Binding
Les 4 types de binding
interpolationeventtwo-way

Angular propose quatre types de data binding : interpolation {{ }}, property binding [], event binding () et two-way binding [()].

HTML
<!-- Interpolation {{ }} -->
<h1>{{ titre }}</h1>
<p>{{ user.nom | uppercase }}</p>
<p>{{ 2 + 2 }}</p>
<p>{{ isLoading ? 'Chargement...' : 'Prêt' }}</p>

<!-- Property binding [ ] -->
<img [src]="user.avatar" [alt]="user.nom" />
<button [disabled]="isLoading">Envoyer</button>
<div [class.active]="isActive"></div>
<div [class]="{'active': isActive, 'error': hasError}"></div>
<div [ngClass]="{'active': isActive, 'bold': isBold}"></div>
<div [style.color]="textColor"></div>
<div [ngStyle]="{'font-size': fontSize + 'px'}"></div>

<!-- Event binding ( ) -->
<button (click)="onClick()">Cliquer</button>
<button (click)="onClick($event)">Avec event</button>
<input (keyup.enter)="onEnter()">
<input (input)="onInput($event)">
<form (ngSubmit)="onSubmit()">

<!-- Two-way binding [( )] — nécessite FormsModule -->
<input [(ngModel)]="nom" />
<!-- Équivalent à : -->
<input [value]="nom" (input)="nom = $event.target.value" />
Directives
Directives structurelles
*ngIf*ngFor*ngSwitch

Les directives structurelles modifient le DOM en ajoutant ou supprimant des éléments. Elles sont préfixées par *.

HTML
<!-- *ngIf -->
<div *ngIf="user">{{ user.nom }}</div>
<div *ngIf="isLoading; else content">
  <app-spinner></app-spinner>
</div>
<ng-template #content>
  <app-user-list [users]="users"></app-user-list>
</ng-template>

<!-- *ngFor -->
<ul>
  <li *ngFor="let user of users; let i = index; trackBy: trackById">
    {{ i + 1 }}. {{ user.nom }}
  </li>
</ul>

<!-- trackBy (optimisation) -->
<!-- dans le composant : -->
<!-- trackById(index: number, user: User) { return user.id; } -->

<!-- *ngSwitch -->
<div [ngSwitch]="statut">
  <p *ngSwitchCase="'loading'">Chargement...</p>
  <p *ngSwitchCase="'error'">Erreur</p>
  <p *ngSwitchCase="'success'">Succès !</p>
  <p *ngSwitchDefault>Inconnu</p>
</div>

<!-- ng-container (sans élément DOM) -->
<ng-container *ngIf="user">
  <h2>{{ user.nom }}</h2>
  <p>{{ user.email }}</p>
</ng-container>

<!-- ng-content (projection de contenu) -->
<!-- Dans le composant card : -->
<div class="card">
  <ng-content select="[card-header]"></ng-content>
  <ng-content></ng-content>
</div>
<!-- Utilisation : -->
<app-card>
  <h2 card-header>Titre</h2>
  <p>Contenu projeté</p>
</app-card>
Services & Injection de dépendances
Créer et injecter un service
@Injectablesingleton

Les services Angular sont des classes singleton injectables. providedIn: 'root' les rend disponibles dans toute l'application sans les déclarer dans un module.

TypeScript
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';

@Injectable({
  providedIn: 'root',  // singleton disponible dans toute l'app
})
export class UserService {
  private apiUrl = 'https://api.exemple.fr';

  constructor(private http: HttpClient) {}

  getAll(): Observable<User[]> {
    return this.http.get<User[]>(`${this.apiUrl}/users`);
  }

  getById(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/users/${id}`);
  }

  create(user: Partial<User>): Observable<User> {
    return this.http.post<User>(`${this.apiUrl}/users`, user);
  }

  update(id: number, user: Partial<User>): Observable<User> {
    return this.http.put<User>(`${this.apiUrl}/users/${id}`, user);
  }

  delete(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/users/${id}`);
  }
}

// Injection dans un composant
@Component({ ... })
export class UsersComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.userService.getAll().subscribe({
      next: (users) => this.users = users,
      error: (err) => console.error(err),
    });
  }

  deleteUser(id: number): void {
    this.userService.delete(id).subscribe(() => {
      this.users = this.users.filter(u => u.id !== id);
    });
  }
}
HttpClient
Requêtes HTTP et opérateurs RxJS
HTTPRxJS

HttpClient retourne des Observable. Les opérateurs RxJS comme map, tap et catchError permettent de transformer et gérer les flux de données.

TypeScript
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable, catchError, map, tap, throwError } from 'rxjs';

// GET avec paramètres
getUsers(page: number, limit: number): Observable<User[]> {
  const params = new HttpParams()
    .set('page', page)
    .set('limit', limit);
  return this.http.get<User[]>('/api/users', { params });
}

// POST avec headers
createUser(user: Partial<User>): Observable<User> {
  const headers = new HttpHeaders({
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${this.authToken}`,
  });
  return this.http.post<User>('/api/users', user, { headers });
}

// Opérateurs RxJS essentiels
getUsers(): Observable<User[]> {
  return this.http.get<User[]>('/api/users').pipe(
    map(users => users.filter(u => u.actif)),    // transformer
    tap(users => console.log('Reçu:', users)),   // effet de bord
    catchError(err => {                           // gestion d'erreur
      console.error('Erreur API:', err);
      return throwError(() => err);
    }),
  );
}
Routing
Configuration des routes
Routeslazy loading

Le routeur Angular associe des chemins URL à des composants. Le lazy loading permet de charger les modules à la demande pour optimiser les performances.

TypeScript
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';

const routes: Routes = [
  { path: '',          component: HomeComponent },
  { path: 'users',     component: UsersComponent },
  { path: 'users/:id', component: UserDetailComponent },
  { path: 'admin',     component: AdminComponent, canActivate: [AuthGuard] },

  // Lazy loading
  {
    path: 'blog',
    loadChildren: () => import('./blog/blog.module').then(m => m.BlogModule),
  },

  { path: '**', component: NotFoundComponent }, // 404
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
HTML
<!-- Navigation -->
<nav>
  <a routerLink="/">Accueil</a>
  <a routerLink="/users" routerLinkActive="active">Utilisateurs</a>
  <a [routerLink]="['/users', user.id]">{{ user.nom }}</a>
</nav>

<!-- Vue principale -->
<router-outlet></router-outlet>
TypeScript
// Récupérer les paramètres de route
import { ActivatedRoute, Router } from '@angular/router';

@Component({ ... })
export class UserDetailComponent implements OnInit {
  constructor(
    private route: ActivatedRoute,
    private router: Router,
  ) {}

  ngOnInit(): void {
    // Paramètre de route (:id)
    const id = this.route.snapshot.paramMap.get('id');

    // Observable (si l'id peut changer sans rechargement)
    this.route.params.subscribe(params => {
      this.loadUser(params['id']);
    });

    // Query params (?page=2)
    const page = this.route.snapshot.queryParamMap.get('page');
  }

  goBack(): void {
    this.router.navigate(['/users']);
  }

  goToUser(id: number): void {
    this.router.navigate(['/users', id], { queryParams: { tab: 'profil' } });
  }
}
Pipes
Pipes intégrés
datecurrencyasync

Les pipes transforment les données dans les templates. Le pipe async souscrit automatiquement aux Observables et gère le désabonnement.

HTML
<!-- Pipes intégrés -->
{{ nom | uppercase }}           <!-- ALICE -->
{{ nom | lowercase }}           <!-- alice -->
{{ nom | titlecase }}           <!-- Alice Dupont -->
{{ texte | slice:0:50 }}        <!-- 50 premiers caractères -->
{{ texte | slice:0:50 }}...

{{ prix | currency:'EUR':'symbol':'1.2-2' }}  <!-- 29,99 € -->
{{ prix | currency:'USD' }}                   <!-- $29.99 -->
{{ nombre | number:'1.0-2' }}                 <!-- 1 234.56 -->
{{ tauxTva | percent:'1.1-1' }}              <!-- 20.0% -->

{{ date | date:'dd/MM/yyyy' }}               <!-- 15/03/2024 -->
{{ date | date:'fullDate' }}                 <!-- vendredi 15 mars 2024 -->
{{ date | date:'medium' }}                   <!-- 15 mars 2024, 14:30:00 -->
{{ date | date:'shortTime' }}               <!-- 14:30 -->

{{ objet | json }}              <!-- pour déboguer -->
{{ liste | keyvalue }}          <!-- itérer sur un objet -->
{{ observable$ | async }}       <!-- souscription automatique -->
Pipe personnalisé
@Pipe

Créez vos propres pipes avec ng g p nom. Un pipe implémente PipeTransform et définit la méthode transform().

TypeScript
// Pipe personnalisé
// ng g p truncate
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit = 100, suffix = '...'): string {
    if (value.length <= limit) return value;
    return value.substring(0, limit) + suffix;
  }
}
// Usage : {{ texte | truncate:50:'…' }}
Formulaires Template-driven
Formulaires pilotés par le template
FormsModulengModel

Les formulaires template-driven utilisent ngModel pour le two-way binding. La logique de validation est définie directement dans le template HTML.

TypeScript
// Nécessite FormsModule dans app.module.ts
// imports: [FormsModule]

@Component({ ... })
export class LoginComponent {
  form = { email: '', password: '' };
  loading = false;

  onSubmit(ngForm: NgForm): void {
    if (ngForm.invalid) return;
    console.log(this.form);
  }
}
HTML
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm)">

  <label for="email">Email</label>
  <input
    id="email"
    type="email"
    name="email"
    [(ngModel)]="form.email"
    required
    email
    #emailRef="ngModel"
  />
  <span *ngIf="emailRef.invalid && emailRef.touched">Email invalide</span>

  <label for="password">Mot de passe</label>
  <input
    id="password"
    type="password"
    name="password"
    [(ngModel)]="form.password"
    required
    minlength="8"
    #pwdRef="ngModel"
  />
  <span *ngIf="pwdRef.errors?.['minlength'] && pwdRef.touched">
    8 caractères minimum
  </span>

  <button type="submit" [disabled]="loginForm.invalid || loading">
    Connexion
  </button>
</form>
Formulaires Réactifs
Reactive Forms avec FormBuilder
ReactiveFormsModuleValidators

Les formulaires réactifs définissent la structure et la validation dans la classe TypeScript. Ils sont plus puissants et testables que les formulaires template-driven.

TypeScript
// Nécessite ReactiveFormsModule dans app.module.ts
import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';

@Component({ ... })
export class RegisterComponent implements OnInit {
  form!: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.form = this.fb.group({
      nom:      ['', [Validators.required, Validators.minLength(2)]],
      email:    ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8)]],
      confirm:  ['', Validators.required],
    }, { validators: this.passwordMatch });
  }

  // Validateur personnalisé
  passwordMatch(control: AbstractControl) {
    const pass = control.get('password')?.value;
    const conf = control.get('confirm')?.value;
    return pass === conf ? null : { mismatch: true };
  }

  get nom()   { return this.form.get('nom'); }
  get email() { return this.form.get('email'); }

  onSubmit(): void {
    if (this.form.invalid) { this.form.markAllAsTouched(); return; }
    console.log(this.form.value);
  }
}
HTML
<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <label>Nom</label>
  <input formControlName="nom" />
  <div *ngIf="nom?.invalid && nom?.touched">
    <span *ngIf="nom?.errors?.['required']">Nom requis</span>
    <span *ngIf="nom?.errors?.['minlength']">2 caractères minimum</span>
  </div>

  <label>Email</label>
  <input formControlName="email" type="email" />
  <span *ngIf="email?.invalid && email?.touched">Email invalide</span>

  <button type="submit" [disabled]="form.invalid">S'inscrire</button>
</form>
Lifecycle Hooks
Cycle de vie d'un composant
OnInitOnDestroy

Angular appelle des méthodes spéciales à chaque étape du cycle de vie d'un composant. Il est crucial de se désabonner dans ngOnDestroy pour éviter les fuites mémoire.

HookMoment d'appel
ngOnChangesAvant ngOnInit, puis à chaque changement d'@Input
ngOnInitUne fois après le premier ngOnChanges
ngDoCheckAprès chaque vérification de changement
ngAfterContentInitAprès l'insertion du contenu (ng-content)
ngAfterContentCheckedAprès chaque vérification du contenu
ngAfterViewInitAprès l'initialisation de la vue du composant
ngAfterViewCheckedAprès chaque vérification de la vue
ngOnDestroyJuste avant la destruction du composant
TypeScript
import { Component, OnInit, OnChanges, OnDestroy, Input, SimpleChanges } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({ ... })
export class ExempleComponent implements OnInit, OnChanges, OnDestroy {
  @Input() userId!: number;
  private subscription!: Subscription;

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['userId']) {
      console.log('userId changé:', changes['userId'].currentValue);
      this.loadUser();
    }
  }

  ngOnInit(): void {
    this.subscription = this.userService.getAll()
      .subscribe(users => this.users = users);
  }

  ngOnDestroy(): void {
    // TOUJOURS se désabonner pour éviter les memory leaks
    this.subscription?.unsubscribe();
  }
}
Modules
NgModule — organiser l'application
@NgModuledeclarations

Les modules Angular regroupent des composants, directives et pipes liés. Chaque déclaration (composant, directive, pipe) doit appartenir à exactement un module.

TypeScript
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [    // Composants, directives, pipes de CE module
    AppComponent,
    UserCardComponent,
    UserListComponent,
    TruncatePipe,
  ],
  imports: [         // Modules dont CE module dépend
    BrowserModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,
    AppRoutingModule,
  ],
  providers: [       // Services (préférer providedIn: 'root')
    UserService,
  ],
  bootstrap: [AppComponent],  // Composant racine (app.module.ts seulement)
})
export class AppModule {}

Aucun résultat pour votre recherche.