Son astuce pour mieux se former

Plonge dans une interview inspirante et condensée de Gérôme Grignon, développeur frontend passionné et figure incontournable de la communauté Angular francophone.

Dans cet échange, Gérôme partage son parcours, ses conseils d'apprentissage, sa vision d'Angular et sa réflexion sur l'usage de l'IA dans le développement web.

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Utiliser le Contexte pour propager des données dans Angular

Imaginez que vous ayez besoin de partager des informations entre un composant parent et plusieurs composants enfants. La méthode traditionnelle consisterait à passer ces données via des input() à chaque niveau, ce qui peut devenir fastidieux et difficile à maintenir.

Le Problème

Prenons un exemple concret : un système d'onglets où chaque onglet doit savoir :

  • Quel onglet est actuellement actif
  • Comment changer l'onglet actif

Sans contexte, vous devriez faire quelque chose comme ceci :

ts
@Component({
  template: `
    <app-tabs-container [activeTab]="activeTab" (tabChange)="onTabChange($event)">
      <app-tab [activeTab]="activeTab" (tabChange)="onTabChange($event)" [id]="0">
        Contenu 1
      </app-tab>
      <app-tab [activeTab]="activeTab" (tabChange)="onTabChange($event)" [id]="1">
        Contenu 2
      </app-tab>
    </app-tabs-container>
  `
})

PROBLÈME

Cette approche crée beaucoup de code répétitif et rend la maintenance plus difficile. Chaque composant doit explicitement recevoir et transmettre les mêmes propriétés.

La Solution avec le Contexte

Au lieu de cela, nous pouvons utiliser un contexte pour partager ces informations automatiquement avec tous les composants enfants. Voici comment :

ts
import { InjectionToken } from '@angular/core';

export interface TabsContext {
  activeTabId: number;
  selectTab: (id: number) => void;
}

export const TABS_CONTEXT = new InjectionToken<TabsContext>('TABS_CONTEXT');
ts
@Component({
  selector: 'app-tabs-container',
  standalone: true,
  template: `
    <div class="tabs-container">
      <ng-content></ng-content>
    </div>
  `,
  providers: [
    {
      provide: TABS_CONTEXT,
      useExisting: TabsContainerComponent
    }
  ]
})
export class TabsContainerComponent implements TabsContext {
    activeTab = input.required<number>();
    tabChange = output<number>();
}
ts
import { input, inject, Component, computed } from '@angular/core';

@Component({
  selector: 'app-tab',
  standalone: true,
  template: `
    @if (isActive()) {
      <div class="tab-content">
        <ng-content></ng-content>
      </div>
    }
  `
})
export class TabComponent {
    protected tabsContext = inject(TABS_CONTEXT);
    id = input.required<number>();
    isActive = computed(() => this.tabsContext.activeTabId() === this.id());
}

Utilisation

Maintenant, l'utilisation devient beaucoup plus simple et propre :

ts
@Component({
  selector: 'app-user-profile',
  standalone: true,
  imports: [TabsContainerComponent, TabComponent],
  template: `
    <app-tabs-container [activeTab]="activeTab" (tabChange)="onTabChange($event)">
      <app-tab [id]="0">
        <h3>Informations Utilisateur</h3>
        <p>Nom: {{ user.name }}</p>
      </app-tab>
      <app-tab [id]="1">
        <h3>Paramètres</h3>
        <p>Email: {{ user.email }}</p>
      </app-tab>
    </app-tabs-container>
  `
})
```