3 votes

Le lien de l'option de sélection du formulaire réactif Angular vers une autre entrée ne change pas

Je veux avoir une sélection avec des options qui sont lues à partir d'autres entrées et quand l'entrée change, ma sélection change aussi.

Toutes les sélections et les entrées sont des formes réactives. Le problème est que lorsque je modifie la valeur des entrées, les options de sélection ne sont mises à jour que lorsque je fixe la valeur des options au contrôle de formulaire. Si je fixe la valeur des options à la valeur du contrôle de formulaire, elles ne sont pas mises à jour.

L'exemple de code est ici : https://stackblitz.com/edit/angular-5zfocw

Modèle :

<form [formGroup]="form">
  <div formArrayName="opts" *ngFor="let opt of form.get('opts').controls; let i = index">
  <div [formGroupName]="i">
    {{i}}:  
    <input type="text" formControlName="name">
  </div>
  </div>
</form>

<form [formGroup]="form">
  <label> Selection:</label>
  <select formControlName="selection">
    <option *ngFor="let opt of form.get('opts').controls; let i=index" [value]="opt.value.name">
      {{opt.value.name}}
    </option>
  </select>
</form>
<hr>

<pre>
 selection: {{ form.get('selection').value | json}}
</pre>

Compoent :

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public form: FormGroup
  constructor(private fb: FormBuilder){
    this.form = this.fb.group({
      opts: this.fb.array([
        this.fb.group({name: ['N0']}),
        this.fb.group({name: ['N1']}),
        this.fb.group({name: ['N2']})
      ]),
      selection: [{value: {}}]
    })
  }
}

Si je change la ligne 13 du modèle HTML en :

<option *ngFor="let opt of form.get('opts').controls; let i=index" [value]="opt">

Ensuite, l'option et l'entrée sont parfaitement synchronisées. Cependant, je ne suis pas en mesure d'obtenir la valeur de la sélection.

Si la ligne est changée en [value]="opt.value.name" puis sélectionner N2, puis changer l'entrée N2, ma sélection sera perdue et la valeur ne sera pas mise à jour.

ce que je veux

Je veux 1. Sélectionner N2 dans le menu déroulant. 2. Changer N2 en quelque chose d'autre comme N22 dans l'entrée. 3. La sélection doit être mise à jour en N22. Et la valeur N22 doit être affichée en bas de la page.

Que dois-je faire ?

4voto

Eliseo Points 4154

Vous devez vérifier quand le tableau change. Pour cela, abonnez-vous à this.form.get('opts').valueChanges. Utilisez la méthode des paires pour récupérer l'ancienne valeur et changer la valeur de la sélection.

En code, après avoir créé le formulaire

this.form.get('opts').valueChanges.pipe(
  startWith(this.form.value.opts),
  pairwise())
  .subscribe(([old,value])=>{
     const index=old?old.findIndex(x=>x.name==this.form.get('selection').value):-1
     if (index>=0)
        this.form.get('selection').setValue(value[index].name,{emit:false})
  })

En stackblitz

Mise à jour si nous pouvons enlever/ajouté une valeur, le tableau change aussi. Nous devons donc prendre en compte le fait que nous supprimons un élément :

this.form.get('opts').valueChanges.pipe(
  startWith(this.form.value.opts),
  pairwise())
  .subscribe(([old, value]) => {
    if (old.length > value.length) { //if we remove some value
                //we search if "selection" is in the new value
      const index = value? value.findIndex(x => x.name == this.form.get('selection').value) : -1;
      if (index == -1) //if not, simple equal to ""
        this.form.get('selection').setValue("", { emit: false })
    }
    else { //Only change the value if we are not removing
      const index = old ? old.findIndex(x => x.name == this.form.get('selection').value) : -1
      if (index >= 0)
        this.form.get('selection').setValue(value[index].name, { emit: false })
    }
  })

voir un nouveau stackblitz

2voto

Jingshao Chen Points 880

Après beaucoup d'essais et d'erreurs, j'ai trouvé une autre solution (ou contournement ?) Je l'affiche ici pour référence.

Au lieu d'utiliser la valeur de la chaîne comme valeur des options, nous pouvons directement utiliser le FormControl comme valeur, et il est connecté à l'entrée, ainsi les deux sont synchronisés ensemble.

https://stackblitz.com/edit/angular-musy61

Cette section dans le fichier HTML, voir [ngValue]="opt"

  <select formControlName="selection">
    <option *ngFor="let opt of opts.controls; let i=index" [ngValue]="opt">
      {{opt.value.name}}
    </option>
  </select>

Essayez de sélectionner N2 de la sélection, puis modifiez N2 dans l'entrée. Voyez comment tout se met à jour ensemble.

Le problème est que lors de la récupération de la valeur, il s'agit d'un contrôle de formulaire. Donc la valeur est dans un encombrant selection.value.value.name

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X