web-dev-qa-db-fra.com

Angular Formulaires réactifs: la liste déroulante Sélection dynamique ne lie pas

J'ai deux tableaux de données: AssociatedPrincipals (données précédemment enregistrées) et ReferencePrincipals (données statiques à remplir dans les contrôles déroulants). Je ne parviens pas à afficher/sélectionner la valeur précédente de AssociatedPrincipals dans un montant dynamique (la plupart des exemples utilisent un seul menu déroulant) de menus déroulants au chargement de la page.

Je ne suis pas sûr de savoir comment configurer le formulaire (code behind et HTML), en particulier pour définir le formControlName de Select. Actuellement, les valeurs statiques de chaque liste déroulante sont remplies, mais je ne parviens pas à associer correctement la valeur sélectionnée.

public ngOnInit() {
    this.factsForm = this.formbuilder.group({
        associatedPrincipals: this.formbuilder.array([]),
        referencePrincipals: this.formbuilder.array([])
    });

    // Data for both of these methods comes from external source...
    var responseData = // HTTP source...
    // Push retrieved data into form
    this.initPrincipals(responseData[0]);
    // Push static data into form
   this.initStaticData(responseData[1]);
}

public initPrincipals(principals?: IAssociatedPrincipal[]): FormArray {
    principals.forEach((principal) => {
 this.associatedPrincipals.Push(this.createPrincipalFormGroup(principal));
    });
}

public initStaticData(response: IReferencePrincipal[]) {
   response.forEach((principal) => {
      this.referencePrincipals.Push(
           this.formbuilder.control({
                code: principal.code,
                canHaveLead: principal.canHaveLead,
                isDuplicate: false
              }));
        });
}

public createPrincipalFormGroup(principal: IAssociatedPrincipal) {
        return this.formbuilder.group({
            code: principal.code,
            canHaveLead: false,
            isDuplicate: false
        });
    }

public get associatedPrincipals(): FormArray {
        return this.factsForm.get('associatedPrincipals') as FormArray;
    }

    public get referencePrincipals(): FormArray {
        return this.factsForm.get("referencePrincipals") as FormArray;
    }

HTML:

 <form novalidate [formGroup]="factsForm">
        <div formArrayName="associatedPrincipals">
             <div *ngFor="let associatedPrincipal of associatedPrincipals.controls; let i=index;" [formGroupName]="i" >
                <select class="form-control create-input"
                        formControlName="i">
                     <option value=null disabled selected hidden>--Select--</option>
                       <option *ngFor="let refPrincipal of referencePrincipals.controls" [ngValue]="refPrincipal">refPrincipal.value.code</option>
                 </select>
             </div>
         </div>
    </form>

J'apprécie vos commentaires!

EDIT: Ajouté Plunker montrant le problème:https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/

5
dotNetkow

Des problèmes dans votre démo

Selon la démonstration que vous avez fournie, plusieurs problèmes sont énumérés ci-dessous:

  • formControlName n'est pas affecté à select
  • Vous liez object pour choisir l'option. 

Pour le premier problème

Puisque vous parcourez associatedPrincipals pour afficher la liste déroulante de manière dynamique. Et associatedPrincipals qui est un formArray qui peut être considéré comme ci-dessous:

associatedPrincipals = {
  "0": FormControl,
  "1": FormControl
}

Donc, vous pouvez simplement assigner i qui est défini à *ngFor expression à formControlName.

<select formControlName="{{i}}" style="margin-top: 10px">
   ...
</select>

Pour le second problème

Tout en liant object à option, Angular comparera la valeur par défaut et la valeur de option par object instance par défaut. 

Vous pouvez définir la même instance (obtenir de la valeur de __Controls de referencePrincipals) pour formControl de associatedPrincipals (en tant que réponse de @Fetra R.). Mais ce n’est pas le moyen le plus pratique, car il faut prendre une certaine logique pour conserver la même instance d’un objet. 

Ici, je vous donnerais une autre solution qui utilise la directive compareWith spécialement conçue pour votre situation actuelle, voir docs.

En utilisant la directive compareWith, il vous suffit de mettre en œuvre une compareFun pour indiquer à Angular comment considérer deux objets (avec des instances différentes) identiques. Ici, vous pouvez inclure comparing object instance et comparing object fields en même temps.

<select formControlName="{{i}}" style="margin-top: 10px" [compareWith]="compareFun">
  <option value=null disabled selected hidden>--Select--</option>
  <option *ngFor="let refPrincipal of referencePrincipals.controls"
     [ngValue]="refPrincipal.value">{{ refPrincipal.value.code }}</option>
</select>

// tell angular how to compare two objects
compareFn(item1, item2): boolean {
  return item1 && item2 ? item1.code === item2.code : item1 === item2;
}

Consultez docs et corrigez demo pour en savoir plus.

8
Pengyy

Vous devez passer exactement la même référence de l'objet qui a renseigné la sélection dans celui sélectionné pour obtenir la valeur sélectionnée.

Ici, vous utilisez une valeur de FormControl dans referencePrincipals pour renseigner votre selectbox. Pour l'obtenir sélectionné, utilisez cet objet:

public createPrincipalFormControl(principal) {
    const selectedFormControl = this.referencePrincipals.controls.find(form => form.value.code === principal.code)
    return this.formbuilder.control(selectedFormControl.value);
}

Plunker de travail. https://plnkr.co/edit/vw3WZ6?p=preview

2
Fetrarij