Replay

Apprendre Angular en 1h

Je me donne un objectif: vous faire découvrir et apprendre Angular en 1 heure: composant, syntaxe dans les templates, les directives, les signaux, les routeurs, les services, l'injection de dépendances, les observables et les requêtes HTTP. Le nécessaire pour faire une application Angular !.

Skip to content

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

Erreur NG0950: Input is required but no value is available yet

L'erreur NG0950 (Input is required but no value is available yet) est une erreur qui se produit lorsqu'un composant tente d'accéder à un input requis avant qu'une valeur ne lui soit assignée.

Comprendre le problème

Imaginez un interrupteur qui doit recevoir de l'électricité avant de pouvoir fonctionner. De la même manière, certains composants Angular ont besoin de recevoir des données (inputs) avant de pouvoir fonctionner correctement. Si vous essayez d'utiliser ces inputs avant qu'ils ne soient disponibles, Angular affiche cette erreur.

Quand cette erreur se produit-elle ?

Cette erreur se produit généralement lorsque :

  1. Vous accédez à un input requis trop tôt dans le cycle de vie du composant
  2. Vous essayez de lire un input requis directement lors de l'initialisation de la classe
  3. Vous accédez à un input dans le constructeur du composant

Un exemple concret

Voici un exemple de code qui produira cette erreur :

ts
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-card',
  standalone: true,
  template: `
    <div class="card">
      <h2>{{ userName }}</h2>
      <p>ID: {{ userId }}</p>
    </div>
  `,
})
export class UserCardComponent {
  // Input requis pour le nom d'utilisateur
  userName = input.required<string>();
  
  // Input requis pour l'ID utilisateur 
  userId = input.required<number>();
  
  // ❌ Erreur : Tentative d'accès à l'input requis avant qu'il ne soit défini
  private userNameUpperCase = this.userName().toUpperCase();
}
ts
import { Component } from '@angular/core';
import { UserCardComponent } from './user-card.component';

@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [UserCardComponent],
  template: `
    <app-user-card [userName]="'Alice'" [userId]="123"></app-user-card>
  `,
})
export class ParentComponent {}

Dans cet exemple, nous tentons d'accéder à userName lors de l'initialisation de la classe pour créer userNameUpperCase, mais à ce moment, l'input n'a pas encore reçu de valeur du composant parent.

Comment corriger cette erreur

Solution 1 : Utiliser des contextes réactifs

La meilleure approche consiste à utiliser des contextes réactifs, comme computed ou directement dans le template :

ts
import { Component, computed, input } from '@angular/core';

@Component({
  selector: 'app-user-card',
  standalone: true,
  template: `
    <div class="card">
      <h2>{{ userName }}</h2>
      <p>ID: {{ userId }}</p>
      <!-- Accès direct dans le template -->
      <small>{{ userName().toUpperCase() }}</small>
    </div>
  `,
})
export class UserCardComponent {
  // Input requis pour le nom d'utilisateur
  userName = input.required<string>();
  
  // Input requis pour l'ID utilisateur 
  userId = input.required<number>();
  
  // ✅ Utilisation de computed pour accéder à l'input de manière réactive
  userNameUpperCase = computed(() => this.userName().toUpperCase());
}

Solution 2 : Utiliser les hooks du cycle de vie

Une autre approche consiste à accéder aux inputs dans ngOnInit ou plus tard dans le cycle de vie :

ts
import { Component, OnInit, input } from '@angular/core';

@Component({
  selector: 'app-user-card',
  standalone: true,
  template: `
    <div class="card">
      <h2>{{ userName }}</h2>
      <p>ID: {{ userId }}</p>
      <small>{{ userNameUpperCase }}</small>
    </div>
  `,
})
export class UserCardComponent implements OnInit {
  // Input requis pour le nom d'utilisateur
  userName = input.required<string>();
  
  // Input requis pour l'ID utilisateur 
  userId = input.required<number>();
  
  userNameUpperCase = '';
  
  ngOnInit() {
    // ✅ Accès sécurisé car ngOnInit est appelé après l'initialisation des inputs
    this.userNameUpperCase = this.userName().toUpperCase();
  }
}

ASTUCE

Préférez toujours l'approche réactive avec computed car elle permettra de maintenir la réactivité si la valeur de l'input change.

Solution 3 : Utiliser un input optionnel avec valeur par défaut

Si l'input n'est pas strictement requis, vous pouvez le rendre optionnel avec une valeur par défaut :

ts
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-card',
  standalone: true,
  template: `
    <div class="card">
      <h2>{{ userName }}</h2>
      <p>ID: {{ userId }}</p>
      <small>{{ userNameUpperCase }}</small>
    </div>
  `,
})
export class UserCardComponent {
  // Input optionnel avec valeur par défaut
  userName = input<string>('Utilisateur');
  
  userId = input.required<number>();
  
  // ✅ Maintenant sécurisé car userName a toujours une valeur
  userNameUpperCase = this.userName().toUpperCase();
}

Bonnes pratiques

  1. N'accédez jamais aux inputs dans le constructeur ou lors de l'initialisation des propriétés de classe.
  2. Utilisez computed pour les transformations des inputs qui doivent rester réactives.
  3. Accédez aux inputs dans ngOnInit ou plus tard si vous n'avez pas besoin de réactivité.
  4. Rendez les inputs optionnels avec valeurs par défaut quand c'est approprié.

ATTENTION

Si vous utilisez une ancienne version d'Angular (avant la version 16), vous utiliserez le décorateur @Input() au lieu de la fonction input(). Les principes restent les mêmes, mais la syntaxe sera différente.