多个ng-select组件数据同步:实现选中项在其他选择器隐藏
解决方案:ng-select选中项在其他选择器隐藏
核心思路
不能直接过滤原数组,要为每个select维护排除自身选中项后的可用列表,同时监听所有select的选中变化,实时更新其他select的可用选项。
步骤1:父组件改造(使用app-selector的组件)
- 跟踪所有select的选中值集合
- 为每个select生成过滤后的items(排除其他select已选的项)
- 监听每个formControl的变化,更新全局选中集合
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-parent-component', templateUrl: './parent-component.component.html' }) export class ParentComponent implements OnInit { levels = [1, 2, 3]; // 示例层级 users = [ { id: 1, name: '张三' }, { id: 2, name: '李四' }, { id: 3, name: '王五' }, { id: 4, name: '赵六' } ]; form: FormGroup; selectedUserIds: Set<number> = new Set(); // 存储所有已选用户ID constructor(private fb: FormBuilder) {} ngOnInit(): void { // 初始化表单 const formControls = this.levels.reduce((controls, level) => { controls[`usersOnLevel${level}`] = this.fb.control([]); return controls; }, {} as Record<string, any>); this.form = this.fb.group(formControls); // 监听所有表单控件变化,更新全局选中集合 Object.keys(this.form.controls).forEach(controlName => { this.form.get(controlName)?.valueChanges.subscribe(selectedIds => { // 先移除当前控件之前的选中项 const prevValue = this.form.get(controlName)?.value || []; prevValue.forEach((id: number) => this.selectedUserIds.delete(id)); // 添加当前控件新选中的项 selectedIds.forEach((id: number) => this.selectedUserIds.add(id)); }); }); } // 为每个select获取过滤后的items:排除其他select已选的项,但保留自身已选的 getFilteredUsers(controlName: string): any[] { const currentSelected = this.form.get(controlName)?.value || []; return this.users.filter(user => { // 如果当前用户在自身已选中,保留;否则检查是否被其他select选中 return currentSelected.includes(user.id) || !this.selectedUserIds.has(user.id); }); } }
步骤2:父组件模板修改
将原固定的[items]='users'替换为动态过滤后的列表,无需额外自定义optionTemplate(若需自定义,直接基于过滤后的列表渲染即可)
<form [formGroup]="form"> <div *ngFor="let level of levels"> <app-selector class="mb-4" [multiple]="true" [hideSelected]="true" [bindLabel]="'name'" [bindValue]="'id'" [items]="getFilteredUsers('usersOnLevel'+level)" [formControlName]="'usersOnLevel'+level"> <ng-template ng-label-tmp let-item='item' let-clear='clear' #labelTemplate> <span class='ng-value-label cursor-pointer'> {{ item.name }}</span> <span class='ng-value-icon right' (click)='clear(item)' aria-hidden='true'>×</span> </ng-template> </app-selector> </div> </form>
原方案无效原因说明
你之前尝试给items添加hasBeenSelected属性未生效,主要问题在于:
- 直接修改原数组属性后,未触发Angular变更检测,视图未更新
- 所有select绑定同一数组引用,修改属性后无法区分“自身选中”和“其他选中”的项,导致逻辑混乱
上述方案通过为每个select生成独立的过滤列表,既保留自身已选项,又隐藏其他select的已选项,完美匹配需求。
内容的提问来源于stack exchange,提问作者Pablo
相关产品推荐
相关产品推荐

