最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular Reactive Forms: Dynamic Select dropdown value not binding - Stack Overflow

programmeradmin2浏览0评论

I have two arrays of data: AssociatedPrincipals (previously saved data) and ReferencePrincipals (static data to populate in dropdown controls). I'm struggling to get the previous value from AssociatedPrincipals to be displayed/selected in a dynamic amount (most examples use a single dropdown) of dropdowns on page load.

I'm not certain how to set up the form (code behind and HTML), especially setting the Select's formControlName. Currently, the static values in each dropdown populate, but I cannot get the selected value to bind properly.

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>

I appreciate any feedback!

EDIT: Added Plunker showing the issue: /

I have two arrays of data: AssociatedPrincipals (previously saved data) and ReferencePrincipals (static data to populate in dropdown controls). I'm struggling to get the previous value from AssociatedPrincipals to be displayed/selected in a dynamic amount (most examples use a single dropdown) of dropdowns on page load.

I'm not certain how to set up the form (code behind and HTML), especially setting the Select's formControlName. Currently, the static values in each dropdown populate, but I cannot get the selected value to bind properly.

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>

I appreciate any feedback!

EDIT: Added Plunker showing the issue: https://embed.plnkr.co/XMLvFUbuc32EStLylDGO/

Share Improve this question edited Sep 12, 2017 at 16:21 dotNetkow asked Sep 11, 2017 at 21:36 dotNetkowdotNetkow 5,3135 gold badges37 silver badges50 bronze badges 3
  • Can you create plunker to demonstrate your issue? – yurzui Commented Sep 12, 2017 at 4:22
  • Yea, I'm going to suggest the same thing. I think we really need to see a working version of your issue with a Plunker. – DeborahK Commented Sep 12, 2017 at 6:54
  • 1 Great suggestion, thanks. Please see hero-list.component HTML and TS. Static data populates in dropdowns which is good, but previously selected Chicago and San Diego values should be set. Instead, the placeholder is set. embed.plnkr.co/XMLvFUbuc32EStLylDGO – dotNetkow Commented Sep 12, 2017 at 16:02
Add a comment  | 

3 Answers 3

Reset to default 12 +150

Problems in your demo

Based on the demo you provided, There are several problems as listed below:

  • There is no formControlName assigned to select.
  • You are binding object to select's option.

For the first problem

Since you are looping through associatedPrincipals to show dropdownlist dynamically. And associatedPrincipals which is a formArray which can consider as below:

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

So you can simply assign i which is defined at *ngFor expression to formControlName.

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

For the second problem

While binding object to option, Angular will compare default value and option's value by object instance by default.

You can set same instance(get from value of referencePrincipals's formControls) to formControl of associatedPrincipals(as @Fetra R.'s answer). But this is not the most convenient way since you have to take some logic to keep the same instance of an object.

Here I would give you another solution which is using compareWith directive designed specifically for your current situation, see docs.

Using compareWith directive, you just need to implement a compareFun to tell angular how to consider two objects(with different instances) as the same.Here yo can include comparing object instance and comparing object fields at the same time.

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

Refer docs and fixed demo to learn detail about it.

You need to pass the exact same reference of the object which populate the select into the selected one to get the selected value.

Here you use a value of all FormControl in referencePrincipals to populate your selectbox, so to get it selected use this object:

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

Working plunker. https://plnkr.co/edit/vw3WZ6?p=preview

There are at least 2 problems with your approach.

  1. Your data source here is probably async. Which means you should not do this.initiPrincipals(responseData[0]) immediately after var responseData but instead in callback of whatever method gets you the data or in a subscription to http service, if you get data through Observable.

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

    If your data comes from @Input() then the right plase is ngOnChanges.

  2. As Fetra pointed out, regardless of the fact that your previously selected option has exactly the same value as the ones you've prepopulated into select list, in order to set it as selected you need exact reference to the ones you've populated it with. so, something like:

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

发布评论

评论列表(0)

  1. 暂无评论