You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular级联下拉异常:新增文档组后子类别选择同步问题

Angular三级级联下拉同步选中问题修复

问题描述

在Angular中实现三级级联下拉功能:选择第一个下拉选项后,第二个下拉加载对应选项;选择第二个下拉选项后,第三个下拉加载对应选项。但存在异常:添加新的文档组后,在某一个文档组中选择子类别,其他文档组的子类别下拉会同步选中相同选项(例如选择Test One>Test 1,新文档组的子类别也会变成Test 1)。

问题根源

所有文档组共用了全局的subCategoryList和typeList数组。当任意一个文档组触发下拉选择时,会修改全局数组内容,而所有文档组的下拉选项都绑定这个全局数组,导致所有组的下拉选项同步变化。

解决方案

为每个文档组维护独立的子类别和类型列表,避免全局变量共享:

  • 新增数组存储每个文档组的下拉状态,包含独立的subCategoryList和typeList
  • 修改级联选择方法,根据当前文档组索引更新对应组的下拉列表
  • 模板中绑定对应文档组的独立下拉列表,而非全局数组

修改后的TS代码

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  FormArray,
  FormBuilder,
  FormControl,
  FormGroup,
  FormsModule,
  ReactiveFormsModule,
} from '@angular/forms';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule, FormsModule, ReactiveFormsModule],
  templateUrl: 'main.html',
})
export class App {
  categoryList: Array<any> = [
    {
      name: "Test One",
      subCategoryList: [
        {
          typeList: [''],
          name: "Test 1",
        },
        {
          typeList: [''],
          name: "Test 1.1",
        },
        {
          typeList: [''],
          name: "Test 2.2",
        },
      ],
    },
    {
      name: "Test Two",
      subCategoryList: [
        {
          typeList: [''],
          name: "Test 2",
        },
        {
          typeList: [''],
          name: "Test 2.1",
        },
        {
          typeList: [''],
          name: "Test 2.2",
        },
      ],
    },
    {
      name: "Test Three",
      subCategoryList: [
        {
          typeList: [''],
          name: "Test 3",
        },
      ],
    },
  ];

  // 为每个文档组维护独立的下拉状态
  documentDropdownStates: Array<{subCategoryList: any[], typeList: any[]}> = [];

  documentForm: FormGroup = this.fb.group({
    appCode: ['123'],
    accountType: [''],
    emailId: ['test@aol.com'],
    IDV: [''],
    envelopeRequest: this.fb.group({
      documents: this.fb.array([]),
    }),
  });

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.initDocument();
  }

  // 根据文档组索引更新子类别列表
  changeCategoryList(comIndex: number, event: any) {
    const selectedCategory = event.target.value;
    const category = this.categoryList.find(cat => cat.name === selectedCategory);
    this.documentDropdownStates[comIndex].subCategoryList = category?.subCategoryList || [];
    // 切换类别时重置类型列表
    this.documentDropdownStates[comIndex].typeList = [];
    // 重置当前组的子类别和类型表单值
    this.documentFields(comIndex).at(comIndex * 2 + 1).get('value')?.setValue('');
  }

  // 根据文档组索引更新类型列表
  changeSubCategoryList(comIndex: number, event: any) {
    const selectedCategory = this.documentFields(comIndex).at(comIndex * 2).get('value')?.value;
    const selectedSubCategory = event.target.value;
    
    const category = this.categoryList.find(cat => cat.name === selectedCategory);
    const subCategory = category?.subCategoryList.find(sub => sub.name === selectedSubCategory);
    this.documentDropdownStates[comIndex].typeList = subCategory?.typeList || [];
  }

  initDocument() {
    this.documents.push(this.newDocument());
    const documentIndex = this.documents.length - 1;
    this.newDocumentFieldGroup(documentIndex);
    // 初始化当前文档组的下拉状态
    this.documentDropdownStates.push({
      subCategoryList: [],
      typeList: []
    });
  }

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

  newDocumentFieldGroup(comIndex: number) {
    this.documentFields(comIndex).push(this.newDocumentFields());
    this.documentFields(comIndex).push(
      this.newDocumentFields('documentSubCategory')
    );
  }

  newDocumentFields(name = 'documentCategory') {
    return this.fb.group({
      name: new FormControl(name),
      value: new FormControl(
        name == 'documentCategory' ? '--Choose Document Category--' : ''
      ),
    });
  }

  get documents() {
    return (this.documentForm.controls.envelopeRequest as FormGroup).controls
      .documents as FormArray;
  }

  documentFields(comIndex: number) {
    return this.documents.controls[`${comIndex}`].get(
      'documentFields'
    ) as FormArray;
  }

  documentFieldsIndexes(comIndex: number) {
    return Array(this.documentFields(comIndex).controls.length / 2)
      .fill(0)
      .map((x, i) => i * 2);
  }

  submit() {
    console.log(this.documentForm.value);
  }
}

bootstrapApplication(App);

修改后的HTML模板

<form [formGroup]="documentForm" (ngSubmit)="submit()">
  
  <ng-container formGroupName="envelopeRequest">
    <button type="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 class="form-select" formControlName="value"  (change)="changeCategoryList(comIndex, $event)">
                      <option>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 class="form-select" formControlName="value" (change)="changeSubCategoryList(comIndex, $event)">
                      <option>Select</option>
                      <option *ngFor="let s of documentDropdownStates[comIndex].subCategoryList">{{s.name}}</option>
                    </select>
                  </ng-container>
                </div>

                <label class="col-auto col-form-label">Type:</label>
                <div class="col-md-2">
                  <select class="form-select">
                    <option>Select</option>
                    <option *ngFor="let t of documentDropdownStates[comIndex].typeList">{{t}}</option>
                  </select>
                </div>
              </div>
            </ng-container>
          </ng-container>
        </ng-container>
      </ng-container>
    </ng-container>
  </ng-container>

  <button type="submit">Submit</button>
</form>
<pre>{{this.documentForm.value | json }}</pre>

内容的提问来源于stack exchange,提问作者fearless924

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 01:09:58