web-dev-qa-db-ja.com

Angular Reactive Forms:Dynamic Select dropdown value not binding

2つのデータ配列があります。AssociatedPrincipals(以前に保存されたデータ)とReferencePrincipals(ドロップダウンコントロールに入力する静的データ)です。ページロード時にドロップダウンの動的な量(ほとんどの例では単一のドロップダウンを使用)で表示/選択されるAssociatedPrincipalsから以前の値を取得するのに苦労しています。

フォーム(コードビハインドとHTML)の設定方法、特にSelectのformControlNameの設定方法がわかりません。現在、各ドロップダウンの静的な値が入力されていますが、選択された値を正しくバインドできません。

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>

フィードバックをありがとう!

編集:問題を示すPlunkerを追加しました:https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/

6
dotNetkow

デモの問題

あなたが提供したデモに基づいて、以下にリストされているようにいくつかの問題があります:

  • formControlNameに割り当てられているselectはありません。
  • objectを選択のオプションにバインドしています。

最初の問題の場合

associatedPrincipalsをループして、ドロップダウンリストを動的に表示しているためです。そしてassociatedPrincipalsformArrayであり、以下のように考えることができます:

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

そのため、*ngFor式で定義されているiformControlNameに割り当てるだけです。

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

2番目の問題の場合

objectoptionにバインドしている間、Angularはデフォルト値とoptionの値をobject instanceで比較しますデフォルトでは。

同じインスタンス(referencePrincipalsのformControlsの値から取得)をassociatedPrincipalsのformControlに設定できます(@Fetra Rの回答として)。ただし、オブジェクトの同じインスタンスを保持するためにいくつかのロジックを取る必要があるため、これは最も便利な方法ではありません。

ここでは、現在の状況に合わせて特別に設計されたcompareWithディレクティブを使用する別のソリューションを紹介します。docsを参照してください。

compareWithディレクティブを使用すると、compareFunを実装してangular 2つのオブジェクト(インスタンスが異なる)を同じものと見なす方法を伝える必要があります。ここでcomparing object instancecomparing object fieldsを同時に含めます。

<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;
}

詳細についてはdocsおよび修正済みdemoを参照してください。

9
Pengyy

選択した値を取得するには、選択したオブジェクトに選択を移入するオブジェクトとまったく同じ参照を渡す必要があります。

ここでは、FormControlのすべてのreferencePrincipalsの値を使用してselectboxに入力します。そのため、このオブジェクトを選択して選択します。

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

働くプランカー。 https://plnkr.co/edit/vw3WZ6?p=preview

2
Fetrarij

アプローチには少なくとも2つの問題があります。

  1. ここのデータソースはおそらく非同期です。つまり、_var responseData_の直後にthis.initiPrincipals(responseData[0])を実行するのではなく、Observableを介してデータを取得する場合は、データを取得するメソッドのコールバックまたはhttpサービスのサブスクリプションで実行する必要があります。

    _let subscription = myservice.getmedata.subscribe(data => { //here you should do your initializations with data from server };_

    データが@Input()からのものである場合、正しい表現はngOnChangesです。

  2. Fetraが指摘したように、以前に選択したオプションは、選択リストに事前に入力したものとまったく同じ値であるという事実に関係なく、選択済みとして設定するには、入力したオプションを正確に参照する必要があります。だから、次のようなもの:

    this.formGroup.controls['yourSelectControl'].patchValue(this.yourInitialCollectionOfOptions.find(v => v.propertyByWhichYouWantToCompare == valueFromServer.propertyByWhichYouWantToCompare)

1
dee zg