如何在复杂嵌套form array中独立禁用各会话已选中值?
嵌套Form Array选择框禁用逻辑跨会话污染修复方案
问题现象
开发Angular嵌套响应式表单(Form Array)选择框禁用功能时,预期效果为每个独立会话内,选择框下拉列表中已被选中的color Type选项自动置灰禁用,但实际运行时,任意会话中选中某个选项,会导致所有其他会话的下拉列表中同值选项同步被禁用,各会话的禁用逻辑没有隔离。
根因分析
- 原有禁用逻辑做选中值统计时,没有限定会话范围,直接收集了整个表单所有会话下的选中值做判断,导致任意会话的选值变更都会影响全局禁用规则
- 模板中所有会话的下拉选项共用同一个全局禁用判断方法,没有绑定到对应会话实例的独立计算逻辑
修复步骤
1. 封装会话维度的禁用选项计算方法
方法入参传入当前会话索引、当前选择框自身的值,仅统计当前会话内其他选择框已选中的值作为禁用集合,不跨会话收集数据:
/** * 获取指定会话下需要禁用的colorType选项 * @param sessionIndex 当前会话在FormArray中的索引 * @param currentControlValue 当前渲染的选择框自身的值,避免把自身选中值误判为禁用 */ getSessionDisabledOptions(sessionIndex: number, currentControlValue: string | null): string[] { // 仅取当前会话对应的FormGroup实例 const currentSessionGroup = this.mainFormArray.at(sessionIndex) as FormGroup; const colorSelectionsArray = currentSessionGroup.get('colorSelections') as FormArray; // 收集当前会话下所有已选的colorType值,过滤空值、当前控件自身的值 const selectedVals = colorSelectionsArray.controls .map(control => control.get('colorType')?.value) .filter(val => !!val && val !== currentControlValue); // 去重后返回 return [...new Set(selectedVals)]; }
2. 模板层绑定当前会话的独立判断逻辑
渲染下拉选项时,传入当前会话索引、当前选择框的控件值,调用上述会话级方法判断禁用状态,不要调用全局通用判断方法:
<!-- 外层遍历所有会话FormGroup --> <ng-container *ngFor="let sessionGroup of mainFormArray.controls; let sIdx = index"> <div [formGroup]="sessionGroup" class="session-block"> <h4>会话 {{sIdx + 1}}</h4> <!-- 内层遍历当前会话下的color选择控件 --> <ng-container *ngFor="let colorControl of sessionGroup.get('colorSelections')['controls']; let cIdx = index"> <div [formGroup]="colorControl" class="select-row"> <select formControlName="colorType"> <option [ngValue]="null">请选择颜色类型</option> <option *ngFor="let opt of colorTypeOptions" [ngValue]="opt.value" <!-- 仅判断当前选项是否在当前会话的禁用集合内 --> [disabled]="getSessionDisabledOptions(sIdx, colorControl.get('colorType').value).includes(opt.value)" > {{opt.label}} </option> </select> </div> </ng-container> </div> </ng-container>
3. 可选性能优化
如果单会话内选择框数量较多,可以在每个会话FormGroup初始化时,单独挂载该会话的值变更监听,缓存当前会话的禁用选项集合,避免每次渲染都全量遍历计算:
// 新建会话表单组的工厂方法 createSessionFormGroup(): FormGroup { const colorSelections = this.fb.array([]); const sessionGroup = this.fb.group({ colorSelections: colorSelections }); // 仅监听当前会话内的选值变更,更新当前会话的禁用缓存 const disabledOptionsCache: Set<string> = new Set(); colorSelections.valueChanges.subscribe(selectionList => { disabledOptionsCache.clear(); selectionList.forEach(item => { if (item.colorType) disabledOptionsCache.add(item.colorType); }); this.cdr.markForCheck(); }); // 把缓存挂载到表单组实例上,方便模板直接读取 (sessionGroup as any)._disabledCache = disabledOptionsCache; return sessionGroup; }
内容的提问来源于stack exchange,提问作者cng
相关产品推荐
相关产品推荐

