J'ai créé un exemple simple pour démontrer un problème étrange auquel je suis confronté.
Stackblitz - https://stackblitz.com/edit/angular-change-detection-form-group
J'ai trois composants et les voici:
1 - composant d'application
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'my-app',
template: `<hello [form]="form"></hello>
<hr />
<button (click)="changeFormValue()">Change Form Value</button>`,
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
name = 'Angular';
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
name: new FormControl('ABC'),
age: new FormControl('24')
});
}
changeFormValue() {
this.form.setValue({
name: 'XYZ',
age: 35
})
}
}
2 - composant bonjour
import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'hello',
template: `<form [formGroup]="form">
<app-input [form]="form"></app-input>
</form>`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloComponent implements OnChanges {
@Input() form: FormGroup;
ngOnChanges(changes) {
console.log(changes)
}
}
3 - composant d'entrée
import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-input',
template: `Name : <input type="text" [formControl]="nameFormcontrol" /> {{nameFormcontrol.value}} <br /><br />
Age : <input type="text" [formControl]="ageFormcontrol" /> {{ageFormcontrol.value}}`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
@Input() form: FormGroup;
nameFormcontrol;
ageFormcontrol;
ngOnInit() {
this.nameFormcontrol = this.form.get('name');
this.ageFormcontrol = this.form.get('age');
}
ngOnChanges(changes) {
console.log(changes)
}
}
Dans le composant bonjour et le composant d'entrée, j'ai défini la stratégie de détection modifiée sur onpush. Comme vous le voyez ci-dessus, je crée une instance de groupe de formulaires dans le composant d'application et la transmets aux composants enfants. Maintenant, lorsque je clique sur le bouton du composant d'application pour modifier la valeur du formulaire, il modifie la valeur dans les champs de saisie, mais pas les textes en clair. Cela ne fonctionne que si je supprime la détection de changement on Push des deux composants enfants. Même ngOnChanges n'est pas appelé même si les valeurs du groupe de formulaires changent.
N'est-ce pas bizarre. Comment fonctionne la détection des modifications pour les entrées et non pour les textes en clair ici?
Quelqu'un pourrait-il m'expliquer cela s'il vous plaît? Et quelle est la solution de contournement sans supprimer la détection de changement onpush.
Au cours de la détection automatique des modifications (cd), exécutez Angular remarquera que les valeurs de votre FormGroup
ont été mises à jour et met à jour l'interface utilisateur respectivement.
En définissant la stratégie de détection des modifications sur OnPush
, vous désactivez la détection automatique des modifications exécutée par Angular. Dans ce cas, seules les valeurs internes sont mises à jour, mais Angular ne vérifierait pas les valeurs de l'interface utilisateur.
C'est peut-être une explication trop superficielle de ChangeDetection d'Angular, donc je recommande ceci blog pour un examen plus approfondi de ce sujet.
ngOnChanges
ne se déclenche pas, car la référence d'objet (adresse mémoire) de votre FormGroup
n'a pas été modifiée. ngOnChanges
n'est déclenché que si vous passez primitives au @Input
de vos composants. Et cela déclencherait également une nouvelle détection de changement exécutée par Angular.
Pour mettre à jour l'interface utilisateur, vous pouvez déclencher la détection des modifications manuellement en injectant ChangeDetectorRef
dans votre composant parent et en appelant detectChanges()
.
Cela pourrait ressembler à ceci:
constructor(private cd: ChangeDetectorRef) {}
...
changeFormValue() {
this.form.setValue({
name: 'XYZ',
age: 35
});
// This will trigger the change detection and your input field are updated
this.cd.detectChanges();
}
J'espère que c'est compréhensible ;-)
Angular ne détecte les changements que si l'adresse mémoire de la variable change. La définition de la valeur ne modifie pas l'adresse mémoire, donc ne frappe pas ngOnChanges.
Il en va de même avec les tableaux. Un Push simple n'atteint pas ngOnChanges, doit changer l'adresse mémoire par = en un nouveau tableau.
Essayez ceci:
import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-input',
template: `
<div formGroupName="form">
Name : <input type="text" formControlName="name" /> {{form.value.name}} <br /><br />
Age : <input type="text" formControlName="age" /> {{form.value.age}}
</div>`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
@Input() form: FormGroup;
ngOnInit() {
}
ngOnChanges(changes) {
console.log(changes)
}
}