Search code examples
angulartypescriptangular-reactive-forms

Same method on dynamic select option in Angular, Is this possible?


I have a dynamic array that has an onChange method on the select option. When I select the option the method works but when I add a new array and select the new option the first array resets. Here is my array:

  <ng-container formGroupName="envelopeRequest">
    <button (click)='initDocument()'>Add Document</button>
    <ng-container formArrayName="documents">
      <ng-container
        *ngFor="let control of documents.controls; let comIndex=index"
      >
        <ng-container [formGroupName]="comIndex">
          <div class="row justify-content-start mb-3">
            <div class="col-md-1">
              {{comIndex}}
              <input
                type="hidden"
                id="fname"
                name="fname"
                formControlName="documentId"
              />
              <label for="formFile" class="col-form-label">File:</label>
            </div>
            <div class="col-md-6">
              <input
                class="form-control"
                type="file"
                id="file"
                name="file"
                formControlName="name"
              />
            </div>
          </div>
          <ng-container formArrayName="documentFields">
            <ng-container
              *ngFor="let skillIndex of documentFieldsIndexes(comIndex);"
            >
              <div class="row mb-3">
                <label class="col-sm-1 col-form-label">Category:</label>
                <div class="col-md-2">
                  <ng-container [formGroupName]="skillIndex">
                    <select id="" class="form-select" formControlName="value"  (change)="changeCategoryList($event)">
                      <option selected>Select</option>
                      <option *ngFor="let c of categoryList">
                        {{c.name}}
                      </option>
                    </select>
                  </ng-container>
                </div>

                <label class="col-auto col-form-label">Sub-Category:</label>
                <div class="col-md-2">
                  <ng-container [formGroupName]="skillIndex + 1">
                    <select id="" class="form-select" formControlName="value" (change)="changeSubCategoryList($event)">
                      <option selected>Select</option>
                      <option *ngFor="let s of subCategoryList">{{s.name}}</option>
                    </select>
                  </ng-container>
                </div>
                <label class="col-auto col-form-label">List:</label>
                <div class="col-md-2">
                  <ng-container [formGroupName]="skillIndex + 2">
                    <select id="" class="form-select" formControlName="value">
                      <option selected>Select</option>
                      <option *ngFor="let name of typeList">{{name}}</option>
                    </select>
                  </ng-container>
                </div>
              </div>
            </ng-container>
          </ng-container>
        </ng-container>
      </ng-container>
    </ng-container>
  </ng-container>

I'm assuming it's because both select options are using the same method now. Here is a snippet of my ts code:

 changeCategoryList(category: any) {
    this.subCategoryList = this.categoryList.find(
      (cat: any) => cat.name == category.target.value
    ).subCategoryList;
  }

  changeSubCategoryList(subList: any) {
    this.typeList = this.categoryList
      .find((cat: any) => cat.name == this.selectedCategoryList)
      .subCategoryList.find(
        (stat: any) => stat.name == subList.target.value
      ).typeList;
  }

Is it possible to have an index on a method? Here is a link to the full code on

StackBlitz


Solution

  • From your HTML, you are sharing the subCategoryList instance for the documentSubCategory select options across the FormGroups in the documents FormArray.

    You should create/use a separate instance for each FormGroup to avoid overwriting the value when selecting the documentSubCategory in another FormGroup.

    Using the comIndex to determine whether the data selection list should fall under which FormGroup. Your subCategoryList will be a multi-dimensional array that holds the respective data for the sub-category data selection according to the comIndex sequence. The concept is the same as the documentType selection.

    subCategoryList: Array<any[]> = [];
    typeList: Array<any[]> = [];
    
    changeCategoryList(category: any, comIndex: number) {
      this.subCategoryList[comIndex] = this.categoryList.find(
        (cat: any) => cat.name == category.target.value
      ).subCategoryList;
    }
    
    changeSubCategoryList(subList: any, comIndex: number) {
      let selectedCategory = this.documentFields(comIndex).get('0')?.get('value')?.value;
          
      this.typeList[comIndex] = this.categoryList
        .find((cat: any) => cat.name == selectedCategory)
        .subCategoryList.find(
          (stat: any) => stat.name == subList.target.value
        ).typeList;      
    }
    

    When adding the document, you should add a new empty array to both subCategoryList and typeList arrays.

    newDocument() {
      this.subCategoryList.push([]);
      this.typeList.push([]);
    
      return this.fb.group({
        name: new FormControl(''),
        documentId: new FormControl(''),
        documentFields: this.fb.array([]),
      });
    }
    

    For the HML, you need to provide the comIndex to the changeCategoryList and changeSubCategoryList methods. And retrieve the respective select options with subCategoryList[comIndex] and typeList[comIndex].

    <select id="" class="form-select" formControlName="value"  (change)="changeCategoryList($event, comIndex)">
      <option selected>Select</option>
      <option *ngFor="let c of categoryList">
        {{c.name}}
      </option>
    </select>
    
    <select id="" class="form-select" formControlName="value" (change)="changeSubCategoryList($event, comIndex)">
      <option selected>Select</option>
      <option *ngFor="let s of subCategoryList[comIndex]">{{s.name}}</option>
    </select>
    
    ...
    
    <select id="" class="form-select" formControlName="value">
      <option selected>Select</option>
      <option *ngFor="let name of typeList[comIndex]">{{name}}</option>
    </select>
    

    Demo @ StackBlitz