1
votes

Comment obtenir FormControlName du champ dont la valeur a changé dans les formulaires réactifs angulaires

J'ai un formulaire réactif avec plus de 10 contrôles de formulaire et en utilisant l'abonnement sur valueChanges observable pour détecter les changements. Cela fonctionne parfaitement mais la sortie est toujours l'objet de valeur de formulaire entier (c'est-à-dire tous les contrôles de formulaire et leurs valeurs). Existe-t-il un moyen d'obtenir simplement le nom du contrôle de formulaire du champ qui a changé?

this.form = this.fb.group({
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
    field7: ['', Validators.required],
    field8: ['', Validators.required],
    field9: ['', Validators.required],
    field10: ['', Validators.required],
    field11: ['', Validators.required],
    field12: ['', Validators.required],
    field13: [{ value: '', disabled: true }, Validators.required]
});

this.form.valueChanges.subscribe(
    result => this.calculateParams(result)
);

calculateParams(result) {
    console.log(result); // giving the entire form.value object
}


0 commentaires

5 Réponses :


2
votes

Vous pouvez isoler le formcontrol du formgroup en utilisant la méthode get et l'accès à la méthode valueChanges sur celui-ci.

this.form.get('formcontrolName').valueChanges() .

Remarque: form.get vous renvoie le AbstractControl , qui contient également la méthode valueChanges .


12 commentaires

à droite, mais pour m'abonner à toutes les valeurs, je devrais m'abonner à chacune d'elles séparément (pour chaque contrôle de formulaire) et je veux éviter cela car j'aurai des formulaires avec beaucoup plus de champs que cela.


vous pouvez extraire la configuration pour fb.group afin que vous puissiez utiliser Object.keys dessus et vous abonner à chaque champ dans une boucle forEach


@dimitri Pouvez-vous essayer this.form.valueChanges().pipe(map(form => form.controls), filter(control => control.dirty)).subscribe() ;


@PhuNgo que de nombreux abonnements (par exemple 50+) sur un seul composant à un moment donné ne seraient-ils pas un goulot d'étranglement de la mémoire?


@KiraAG - n'obtient aucun résultat ( Cannot read property 'dirty' of undefined ). Quelle est la chaîne de pensées ici?


L'idée est d'obtenir les contrôles de formulaire et de les filtrer à l'aide de la propriété dirty , qui filtre les contrôles dont les valeurs ont été modifiées dans l'interface utilisateur.


Ok Désolé, j'ai mal lu form.controls renvoie le tableau, il vous renvoie en fait un objet. Pouvez-vous essayer this.form.valueChanges().pipe(map(form => Object.values(form.controls)), filter(control => control.dirty)).subscribe() . Vous obtiendrez maintenant un tableau de form controls de form controls .


@dimitri s'abonne à chaque champ de la boucle forEach, pas à l'ensemble du groupe. Mais vous avez fait un bon point avec le problème des goulots d'étranglement


@KiraAG - mais cela ne renvoie-t-il pas simplement toutes les valeurs "sales", c'est-à-dire tous les champs qui ont été "touchés"? Que faire si je décide de basculer entre les champs et de continuer à les modifier?


N'est-ce pas ce que vous vouliez?


@KiraAG désolé si la question n'était pas claire, mais non - juste un seul champ dont la valeur a été mise à jour en dernier


Oh, je pense que c'est une exigence un peu complexe, pour laquelle vous aurez peut-être besoin d'un opérateur par paires.



1
votes

Je n'ai pas entièrement testé le code, mais l'idée est d'associer les contrôles et leur clé, puis d'écouter les valueChanges de valueChanges sur chaque contrôle simultanément et de renvoyer la clé d'objet au lieu de la valeur (et bien sûr, vous pouvez mapper à la fois la valeur et la clé à la sortie)

const fields={
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
}

zip(
 from(Object.values(fb.group(fields).controls)),
 from(Object.keys(fields))
).pipe(mergeMap([control,key])=>control.valueChanges.pipe(mapTo(key)))


0 commentaires

2
votes

C'est une solution de rechange, mais si vous stockez les anciennes valeurs, vous pouvez en faire comme

this.old={...this.myForm.value}
this.myForm.valueChanges.subscribe(res=>{
  const key=Object.keys(res).find(k=>res[k]!=this.old[k])
  this.old={...this.myForm.value}
})


2 commentaires

Cela semble faire l'affaire. Au départ, j'avais pensé à quelque chose de similaire mais j'espérais qu'il y avait peut-être une solution "plus luisante" .. Merci!


Dans Angular 6 et Angular 7, il y avait une différence entre myform.get ('...'). Value et "res", ou entre res et myform.value [...], (même si je pense que vous pouvez abonnez-vous à (res, old) => {..}) mais dans Angular 8 il semble qu'il n'y a pas de différence. Vraiment je ne sais pas s'il y a une meilleure solution



0
votes

Vous pouvez vous abonner à chaque modification individuellement.

for (const controlProperty in this.myForm.controls) {
    if (this.myForm.controls.hasOwnProperty(controlProperty)) {
      this.myForm.controls[controlProperty].valueChanges.pipe(untilDestroyed(this)).subscribe(result => {
        console.log(controlProperty + " is now ", result);
      });
    }
}

Cela me paraissait juste plus anguleux.


0 commentaires

0
votes

La manière rxjs de la réponse d'Eliseo

this.form.valueChanges.pipe(
    startWith(this.form.value),
    pairwise(),
    map(([oldValues, newValues]) => {
        return Object.keys(newValues).find(k => newValues[k] != oldValues[k]);
    }),
).subscribe(key => {
    console.log( key )
});


0 commentaires