Appearance
Gérer le responsive design avec BreakpointObserver avec Angular CDK
Le responsive design est essentiel pour créer des applications web modernes qui s'adaptent à tous les écrans. Ici, nous allons voir comment utiliser BreakpointObserver
d'Angular CDK pour gérer dynamiquement l'affichage de notre application.
Installation d'Angular CDK
Commençons par installer Angular CDK :
bash
npm install @angular/cdk
Qu'est-ce que Angular CDK ?
Angular CDK est un ensemble d'outils de développement qui fournit des composants réutilisables et des services pour créer des fonctionnalités complexes dans les applications Angular. Il offre des solutions pour la gestion des overlays, du drag & drop, des tables virtuelles, de l'accessibilité et bien d'autres fonctionnalités, sans imposer de style visuel spécifique. C'est la base sur laquelle Angular Material est construit, mais il peut être utilisé indépendamment pour créer vos propres composants personnalisés.
Implémentation
Voici comment utiliser BreakpointObserver dans un composant :
ts
import { Component } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { inject } from '@angular/core';
import { map } from 'rxjs';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-user',
standalone: true,
imports: [AsyncPipe],
template: `
@if (isDesktop$ | async) {
<div class="desktop-view">
<h2>Vue Desktop</h2>
</div>
} @else {
<div class="mobile-view">
<h2>Vue Mobile</h2>
</div>
}
`
})
export class UserComponent {
private breakpointObserver = inject(BreakpointObserver);
isDesktop$ = this.breakpointObserver
.observe([Breakpoints.Tablet, Breakpoints.Large, Breakpoints.XLarge])
.pipe(map(result => result.matches));
}
this.breakpointObserver.observe([...])
:- Cette méthode utilise le
BreakpointObserver
pour surveiller les changements de taille d'écran. - Les breakpoints spécifiés (
Breakpoints.Tablet
,Breakpoints.Large
,Breakpoints.XLarge
) correspondent à différentes largeurs d'écran. - Le
BreakpointObserver
émet un événement chaque fois que l'écran entre ou sort de l'un de ces breakpoints.
- Cette méthode utilise le
.pipe(map(result => result.matches))
:pipe
est une méthode utilisée pour transformer les données émises par un observable.map
est un opérateur qui prend chaque valeur émise par l'observable et la transforme.- Dans ce cas,
result.matches
est un booléen qui indique si l'écran correspond à l'un des breakpoints spécifiés. Si c'est le cas,result.matches
seratrue
, sinonfalse
.
isDesktop$ | async
: LeAsyncPipe
souscrit à l'observableisDesktop$
et met à jour le template chaque fois que la valeur change.- Cela permet de réagir dynamiquement aux changements de taille d'écran sans avoir à gérer manuellement la souscription et la désinscription de l'observable.
En savoir plus : AsyncPipe
En utilisant un signal
Les signaux offrent une alternative moderne aux observables pour gérer l'état réactif. Voici comment utiliser BreakpointObserver
avec les signaux :
ts
import { Component } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { inject } from '@angular/core';
import { map } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-user',
standalone: true,
template: `
@if (isDesktop()) {
<div class="desktop-view">
<h2>Vue Desktop</h2>
</div>
} @else {
<div class="mobile-view">
<h2>Vue Mobile</h2>
</div>
}
`
})
export class UserComponent {
private breakpointObserver = inject(BreakpointObserver);
isDesktop = toSignal(
this.breakpointObserver
.observe([Breakpoints.Tablet, Breakpoints.Large, Breakpoints.XLarge])
.pipe(map(result => result.matches)),
{ initialValue: false }
);
}
AVANTAGES DES SIGNAUX
- Syntaxe plus simple dans le template (pas besoin du pipe async)
- Meilleure performance car moins de cycles de détection de changements
INITIALVALUE
N'oubliez pas de spécifier une initialValue
lors de la conversion d'un observable en signal pour éviter les valeurs undefined au démarrage de l'application.
Vous pouvez également créer un service utilisant les signaux :
ts
import { Injectable, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { map } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
@Injectable({
providedIn: 'root'
})
export class LayoutService {
private breakpointObserver = inject(BreakpointObserver);
isDesktop = toSignal(
this.breakpointObserver
.observe([Breakpoints.Tablet, Breakpoints.Large, Breakpoints.XLarge])
.pipe(map(result => result.matches)),
{ initialValue: false }
);
}
ts
import { AsyncPipe } from '@angular/common';
import { LayoutService } from './layout.service';
@Component({
selector: 'app-user',
standalone: true,
template: `
@if (isDesktop()) {
<div class="desktop-view">
<h2>Vue Desktop</h2>
</div>
} @else {
<div class="mobile-view">
<h2>Vue Mobile</h2>
</div>
}
`
})
export class UserComponent {
private layoutService = inject(LayoutService);
isDesktop = this.layoutService.isDesktop;
}
Bonnes pratiques
Unsubscribe : N'oubliez pas de vous désabonner de l'observable si vous l'utilisez directement dans le code (bien que dans notre exemple, le pipe async gère cela automatiquement).
Performance : Évitez de créer trop d'observateurs de breakpoints dans votre application. Idéalement, créez un service qui centralise cette logique (voir l'exemple ci-dessus).
Les breakpoints disponibles
BreakpointObserver propose plusieurs breakpoints prédéfinis :
Breakpoint | Media Query | Description |
---|---|---|
XSmall | (max-width: 599.98px) | Pour les très petits écrans (smartphones en mode portrait) |
Small | (min-width: 600px) and (max-width: 959.98px) | Pour les petits écrans (smartphones en mode paysage et petites tablettes) |
Medium | (min-width: 960px) and (max-width: 1279.98px) | Pour les écrans moyens (tablettes et petits ordinateurs portables) |
Large | (min-width: 1280px) and (max-width: 1919.98px) | Pour les grands écrans (ordinateurs de bureau standards) |
XLarge | (min-width: 1920px) | Pour les très grands écrans (moniteurs haute résolution) |
Handset | (max-width: 599.98px) and (orientation: portrait), (max-width: 959.98px) and (orientation: landscape) | Pour les appareils mobiles, quelle que soit l'orientation |
Tablet | (min-width: 600px) and (max-width: 839.98px) and (orientation: portrait), (min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape) | Pour les tablettes, en tenant compte de l'orientation |
Web | (min-width: 840px) and (orientation: portrait), (min-width: 1280px) and (orientation: landscape) | Pour les écrans d'ordinateur, quelle que soit l'orientation |
HandsetPortrait | (max-width: 599.98px) and (orientation: portrait) | Pour les appareils mobiles en mode portrait uniquement |
TabletPortrait | (min-width: 600px) and (max-width: 839.98px) and (orientation: portrait) | Pour les tablettes en mode portrait uniquement |
WebPortrait | (min-width: 840px) and (orientation: portrait) | Pour les écrans d'ordinateur en mode portrait |
HandsetLandscape | (max-width: 959.98px) and (orientation: landscape) | Pour les appareils mobiles en mode paysage uniquement |
TabletLandscape | (min-width: 960px) and (max-width: 1279.98px) and (orientation: landscape) | Pour les tablettes en mode paysage uniquement |
WebLandscape | (min-width: 1280px) and (orientation: landscape) | Pour les écrans d'ordinateur en mode paysage |
PERSONNALISATION
Vous pouvez aussi créer vos propres breakpoints personnalisés :
ts
const customBreakpoint = '(min-width: 500px)';
this.breakpointObserver.observe([customBreakpoint])
ATTENTION
Les breakpoints doivent être choisis en fonction des besoins spécifiques de votre application. Ne vous contentez pas de copier-coller les valeurs par défaut sans réflexion.