简体   繁体   English

如何在 Angular 8 中创建动态下拉菜单

[英]How to create dynamic dropdowns in Angular 8

In My UI, Each Row has level1,level2 and level3 dropdown along with "Add Level" Button.在我的 UI 中,每一行都有 level1、level2 和 level3 下拉菜单以及“添加级别”按钮。

I want on click on "Add Level" button, it will add a new set of three dropdown level1,level2 and level3 in the same row.我想单击“添加级别”按钮,它将在同一行中添加一组新的三个下拉菜单 level1、level2 和 level3。 As much as user click the "Add level" Button, every time it will dynamically add new set of dropdown in same row.就像用户单击“添加级别”按钮一样,每次它都会在同一行中动态添加新的下拉列表。

Other row dropdown will not impact with this.其他行下拉菜单不会对此产生影响。

Can anyone please help to modify my old code to work as my expected output.任何人都可以帮助修改我的旧代码以作为我的预期输出。

StackBlitz Code URL StackBlitz 代码 URL

Please refer the attached image for my expected output for level1 level2 and level3 dropdowns请参阅所附图片,了解我对 level1 level2 和 level3 下拉菜单的预期输出

在此处输入图像描述

It's always the same.它总是一样的。 To control any object using ReactiveForms, you create a FormGroup for Object and FormArray for array.要使用 ReactiveForms 控制任何对象,您可以为 Object 创建一个 FormGroup,为数组创建一个 FormArray。 If the Array is an object, you create a FormArray of FormGroup, else you create a FormArray of FormGroup如果 Array 是一个对象,则创建 FormGroup 的 FormArray,否则创建 FormGroup 的 FormArray

The first is decided the structure of your data, as "levels" is an array, your data can be, eg首先是决定数据的结构,因为“级别”是一个数组,您的数据可以是,例如

{
  "plannedProduction": 210468,
  "factORLossTree": [
    {
      "skuid": "000000000034087070",
      "skuDescription": "SUNLIGHT DESTINY HW PWD YELLOW 8X900G",
      "productionOrderNo": "X49033251",
      "category": "Fabric Cleaning",
      "levels": [
        {
          "level1": "",
          "level2": "",
          "level3": ""
        }
        ....
      ]
    },
    {
      "skuid": "000000000067141695",
      "skuDescription": "SUNLIGHT DESTINY YELLOW 24X170G",
      "productionOrderNo": "X49039793",
      "category": "Fabric Cleaning",
      "levels": [
        {
          "level1": "",
          "level2": "",
          "level3": ""
        }
        ....
      ]
    },
    ....
   ]
}

As always you have a FormArray you use a getter (*) you still have in your code与往常一样,你有一个 FormArray,你使用你的代码中仍然有的 getter (*)

  get lossFormArray() {
    return this.orLosstreeForm.get('factORLossTree') as FormArray;
  }

As we have a FormArray inside a FormArray to get the inner FormArray we need another "getter".因为我们在 FormArray 中有一个 FormArray 来获取内部 FormArray,所以我们需要另一个“getter”。 Well, can not be a getter because we need pass as argument the "index" of the formArray好吧,不能成为 getter,因为我们需要将 formArray 的“索引”作为参数传递

  getLevels(index:number)
  {
    return this.lossFormArray.at(index).get('levels') as FormArray;
  }

we can use also an auxiliar function that return the value of the formArray at index to simplyfied the .html我们还可以使用一个辅助函数,该函数返回 index 处的 formArray 的值以简化 .html

  getItemValue(index:number)
  {
    return this.lossFormArray.at(index).value;
  }

Finally we are add a function to create the formGroup "level"最后我们添加一个函数来创建 formGroup “级别”

createLevel() {
    return this.fb.group({
      level1: [''],
      level2: [''],
      level3: [''],
    });

We are going now to create the formGroup change a bit your function setData我们现在要创建 formGroup 改变一下你的函数 setData

  setData() {
    const resp = {...}
    if (resp.factORLossTree.length > 0) {
      this.orLosstreeForm.patchValue({
        plannedProduction: resp.plannedVolume,
      });
      this.lossFormArray.clear();
      for (const item of resp.factORLossTree) {
        const data = {
          skuid: item.skuid,
          skuDescription: item.skuDescription,
          productionOrderNo: item.productionOrderNo,
          category: item.category,
         
          //see how transform the data to creaate the array "levels"
          levels: [
            {
              level1: item.level1,
              level2: item.level2,
              level3: item.level3,
            },
          ],
        };
        this.addItem(this.createLevelItem(data));
      }
    }
  }

And your function cteateLevelItem:还有你的函数 cteateLevelItem:

createLevelItem(data) {
    const formgrp = this.fb.group({
      skuid: ['', Validators.required],
      ...

      //see how levels is a formArray, at first with an unique element
      //we use the before function createLevel()
      levels: this.fb.array([this.createLevel()]),
    });
    if (data !== null) {
      formgrp.patchValue(data);
    }
    return formgrp;
  }

A complex part know is the .html, but I want to show how create a series of dropdown mutually exclusive.一个复杂的部分知道是.html,但我想展示如何创建一系列互斥的下拉列表。 It's like this SO , but in this case we are going to have an array of arrays of arrays.就像这样SO ,但在这种情况下,我们将有一个数组数组。 Don't worry, make it slow is not so difficult as looks like别担心,让它变慢并不像看起来那么难

First we have a lsit of possibles levels and declare an empty array首先,我们有一个可能的级别并声明一个空数组

  allLevels = ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5'];

  levelList: any[] = [];

Then we are going to fill the array levelList.然后我们将填充数组 levelList。 The adecuate place is in the function "createLevel".合适的地方是函数“createLevel”。 In this function we are subscribe to the valueChanges of the group.在这个函数中,我们订阅了组的 valueChanges。 but we need know the index if the level.但是我们需要知道级别的索引。 So the function becomes like所以函数变得像

  createLevel(index: number) {

    //we are going to create some like
    //levelList[i][j][0], levelList[i][j][1], levelList[i][j][2]

    const j = this.orLosstreeForm && 
              this.lossFormArray && 
              this.lossFormArray.at(index)?
                   this.getLevels(index).controls.length:0;
    const group = this.fb.group({
      level1: [''],
      level2: [''],
      level3: [''],
    });
    group.valueChanges
      .pipe(startWith(null), takeUntil(this.active))
      .subscribe((res: any) => {
          res = res || { level1: null, level2: null };
          if (!this.levelList[index]) this.levelList[index] = [];

          this.levelList[index][j] = [];

          this.levelList[index][j][0] = this.allLevels;
          this.levelList[index][j][1] = this.allLevels.filter(
            (x) => x != res.level1
          );
          this.levelList[index][j][2] = this.allLevels.filter(
            (x) => x != res.level1 && x != res.level2
          );

          //And we check if there any repeat

          if (this.levelList[index][j][1].indexOf(res.level2) < 0)
            group.get('level2').setValue(null, { emitEvent: false });

          if (this.levelList[index][j][2].indexOf(res.level3) < 0)
            group.get('level3').setValue(null, { emitEvent: false });
      });
    return group;
  }

Puff, I know is too large, but be patient, the last part is the .html噗,我知道太大了,但请耐心等待,最后一部分是.html

<div style="background-color: white;">
  <form [formGroup]="orLosstreeForm" *ngIf="orLosstreeForm">
    ...
    <!-- Start of OR loss tree table -->
    <div class="selection-area" formArrayName="factORLossTree">
      <div
        *ngFor="let levelItem of lossFormArray.controls;let i=index" [formGroupName]="i"
      >
        <ng-container>
           ...here we can use getItemValue(i).skuDescription
              or getItemValue(i).skuid or ...

                  <div class="deviations" formArrayName="levels">

                      <div class="row" *ngFor="let group of getLevels(i).controls;
        let j=index" [formGroupName]="j">
                        <div class="col-md-3">
                          <mat-form-field>
                            <mat-label>Level 1</mat-label>
                            <mat-select
                              name="level1"
                              formControlName="level1"
                            >
                              <mat-option value="">--Select--</mat-option>
                                <mat-option
                                  *ngFor="let item of levelList[i][j][0]"
                                  [value]="item"
                                >
                                  {{ item }}
                                </mat-option>
                            </mat-select>
                          </mat-form-field>
                        </div>
                        <div class="col-md-3">
                          <mat-form-field>
                            <mat-label>Level 2</mat-label>
                            <mat-select
                              name="level2"
                              formControlName="level2"
                            >
                              <mat-option value="">--Select--</mat-option>
                                <mat-option
                                  *ngFor="let item of levelList[i][j][1]"
                                  [value]="item"
                                >
                                  {{ item }}
                                </mat-option>

                            </mat-select>
                          </mat-form-field>
                        </div>
                        <div class="col-md-3">
                          <mat-form-field>
                            <mat-label>Level 3</mat-label>
                            <mat-select name="level3"
                              formControlName="level3"
                            >
                              <mat-option value="">--Select--</mat-option>

                               <mat-option
                                  *ngFor="let item of levelList[i][j][2]"
                                  [value]="item"
                                >
                                  {{ item }}
                                </mat-option>
                            </mat-select>
                          </mat-form-field>
                        </div>

                        <div class="col-md-3">
                          <button
                            class="resetButton"
                            aria-describedby="Reset SKU"
                            (click)="getLevels(i).push(createLevel(i))"
                          >
                            Add Level
                          </button>
                        </div>
                    </div>
            ....
    </div>
  </form>
</div>

Se how to add a new group of levels we only need the看看如何添加一组新的关卡,我们只需要

<button (click)="getLevels(i).push(createLevel(i))">
    Add Level
</button>

The final stackblitz 最后的堆栈闪电战

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM