Buliding dropdown/select dynamically via reactive

2019-05-11 22:58发布

问题:

I have multiple (any number) dropdowns on the form that i need to build dynamically.

All these must have a value selected, so need to apply required validator.

Sample json

  [
    {
      "FeatureCaption": "Style",
      "SelectedValue": "shaker",
      "FeatureList": [
        {
          "FeatureId": "CHOOSE",
          "FeatureName": "Choose",
          "IsSelected": true
        },
        {
          "FeatureId": "shaker",
          "FeatureName": "Shaker",
          "IsSelected": false
        }
      ]
    },
    {
      "FeatureCaption": "Material",
      "SelectedValue": "std",
      "FeatureList": [
        {
          "FeatureId": "CHOOSE",
          "FeatureName": "Choose",
          "IsSelected": true
        },
        {
          "FeatureId": "std",
          "FeatureName": "Standard",
          "IsSelected": false
        }
      ]
    }
  ]

These features are available on the form via this.features

Component code to get data and to build form

  onInit() {

    //init form, so angular will complain about formgroup since template is going first
    this.inItForm();

    this.dataSubscription = this.projectSubService.getProjectSubConfig(this.subId).subscribe(
      res => {
        this.features = res.Features;

        //push in features
        this.pushInFormFeatures();
      },
      error => {
        //do error handling part...
      }
    );

  }

inItForm method

  inItForm() {
    //will be putting the config as FormArray
    let configFeatures = new FormArray([]);

    //loop through the features and push into configFeatures FormArray above
    //this.features.forEach(i =>
    //  configFeatures.push(
    //    new FormGroup({
    //      'config': new FormControl({ value: i.SelectedValue, options: i.FeatureList, label: i.FeatureCaption }, Validators.required)
    //    })
    //  )
    //);

    //now create the main FormGroup and add config features to it
    this.projectForm = new FormGroup({
      'features': configFeatures
    });
  }

Push in form features method

  pushInFormFeatures() {
    this.features.forEach(i =>
      (<FormArray>this.projectForm.get('features')).push(
        new FormGroup({
          'config': new FormControl({ value: i.SelectedValue, options: i.FeatureList, label: i.FeatureCaption }, Validators.required)
        })
      )
    );
  }

and html

  <form [formGroup]="projectForm" (ngSubmit)="onSubmit()">

      <div class="form-row">
        <div class="col">

          <div formArrayName="features">

            <div class="form-group form-row" *ngFor="let ic of projectForm.get('features'); let i=index;" [formGroupName]="i">
              <div class="col-lg-2 text-right"><label for="{{i}}" class="col-form-label">{{ic.label}} <app-required-star></app-required-star></label></div>
              <div class="col-lg-10">
                <select class="form-control" id="{{i}}" formControlName="config" [appAutoFocus]="">
                  <!--<option *ngFor="let op of ic.options" [value]="op.FeatureId">{{op.FeatureName}}</option>-->
                </select>
                <!--<span class="mwk-validation-error form-text" *ngIf="!projectForm.get('features')[{{i}}].valid && projectForm.get('features')[{{i}}].errors?.required">Required</span>-->
              </div>
            </div>

          </div>

        </div>
     </div>


    <button type="submit" class="btn btn-primary btn-sm">Submit</button>

  </form>

I am getting following error

    ERROR Error: "Cannot find a differ supporting object '[object Object]' 
of type 'object'. NgFor only supports binding to Iterables such as Arrays."

Obviously i am not building the formArray properly including select options.

How can i build some thing like this? I have been looking for an example and haven't been able to find one to use.

Update 1

For now using template driven approach till i figure out how to do above

  <form (ngSubmit)="onSubmit(f)" #f="ngForm">

    <div class="form-group form-row" *ngFor="let f of features; let i=index;">
      <div class="col-lg-2 text-right"><label for="{{f.FeatureId}}" class="col-form-label">{{f.FeatureCaption}} <app-required-star></app-required-star></label></div>
      <div class="col-lg-10">
        <!--custom-select-->
        <select class="form-control" id="{{f.FeatureId}}" name="{{f.FeatureId}}" [appAutoFocus]="i === 0" ngModel required>
          <option  value="" selected>Choose...</option>
          <option *ngFor="let fl of f.FeatureList" [value]="fl.FeatureId" [selected]="fl.IsSelected">{{fl.FeatureName}}</option>
        </select>
      </div>
    </div>


    <div class="row mt-2">
      <div class="col-lg-2 text-right">&nbsp;</div>
      <div class="col-lg-10">

        <button type="submit" class="btn btn-primary btn-sm" [disabled]="!f.valid">Submit</button>

      </div>
    </div>

  </form>