Comment détecter qu'une valeur @Input() change dans un composant enfant sous Angular ?

Le framework Angular utilise un système de composants pour gérer chaque aspect de votre application ou site internet. Lorsque vous créez un élément HTML, il est possible de surveiller les changements de valeur pour que votre application réagisse à la modification. Il existe pour réaliser cela plusieurs méthodes.

Voici un exemple de composant pour lequel on souhaite détecter un changement.

@Component({
  selector: 'mon-composant',
  template: '<p>Voici le composant pour lequel on souhaite détecter les changements</p>
  <input [(ngModel)]="monComposant.libelle" placeholder="libelle"/>'
})

La méthode "ngOnChanges" permet de détecter les changements sur un composant. Elle présente l'avantage de pouvoir gérer plusieurs éléments en même temps. Si vous avez besoin de gérer plusieurs changements, ce sera cette méthode qu'il faudra utiliser. Votre composant doit implémenter l'interface "onChanges".

export class monComposantComponent implements OnChanges

Déclarez votre méthode ngOnChanges() dans le composant et insérez votre code dans cette méthode. Cette méthode a un autre avantage : elle fonctionne selon le cycle de vie de l'application. Cela signifie qu'elle mémorise également l'ancienne valeur lors du changement.

ngOnChanges(changements: SimpleChanges) {
  //Insérez votre code de détection du changement ici
  console.log(changements.libelle.currentValue); //Valeur actuelle du libellé (après le changement)
  console.log(changements.libelle.previousValue); //Ancienne valeur du libellé (avant le changement)
}

La deuxième solution possède moins d'avantages, mais est plus simple. Il s'agit d'implémenter un "setter" pour votre attribut. Cette méthode ne fonctionne que sur un seul élément et ne peut pas récupérer la précédente valeur. Vous devez déclarer la valeur de l'élément comme un attribut privé de votre composant.

private _libelle: string;
Vous devez ensuite écrire un "setter" accompagné d'un "getter" dans votre composant. Le code du changement sera alors situé dans le "setter".
@Input() set libelle(valeur: string) {
   this._libelle =valeur;
  //Insérez votre code de détection du changement ici
}
get libelle(): string {
  return this._libelle;
}

Vous devez faire également attention à la valeur que vous utilisez lors de la détection. Le framework fonctionne bien pour détecter des types JavaScript basiques (string, number, boolean…) mais la détection du changement n'a pas lieu pour des objets ou des tableaux. En effet, la détection utilise l'opérateur "===". Lors d'un changement dans un tableau ou un objet, cela ne fonctionne pas. Il faut soit implémenter soi-même la méthode "ngDoCheck()" (https://angular.io/guide/lifecycle-hooks#docheck) du framework pour modifier le code de détection pour votre composant, soit assigner un nouveau tableau ou nouvel objet pour provoquer la détection.

Le système de détection ne prend pas en compte les changements effectués par des éléments extérieurs au framework. Vous devez dans ce cas utiliser les méthodes "zone.run()" (https://angular.io/api/core/NgZone) ou bien "ChangeDetectorRef.detectChanges()" (https://angular.io/api/core/ChangeDetectorRef).

AngularJS