Angular中实现Select动态过滤已选Option的完整方案

作者:键盘侠

时间:2026-10-31

浏览:307

本文介绍在Angular中实现人员投票排名场景的方法。通过FormBuilder动态生成多个Select组件,利用FormArray管理表单状态,并订阅valueChanges事件。当用户选择某个Option后,系统自动收集已选人员ID,从剩余候选列表中过滤掉这些ID,确保每个名次只能选择不同人员,避免重复选择。

文章目录

引言

在人员投票排名的业务场景中,通常需要让用户为每个名次选择对应的人员。这就要求Select下拉框具备动态过滤功能:每当用户选定一个Option(即某位人员)后,该人员应从其他所有Select框的可选列表中移除,确保同一人员不会被重复选中到不同名次中。

效果浏览

引言

核心思路

关键在于监听表单值的实时变化。我们需要将当前所有已选中的人员ID收集到一个数组中,然后遍历该数组,利用这些ID作为过滤条件,从原始人员列表中剔除已选中的人员,从而动态更新每个Select框的可用Option列表。

实现步骤

根据实际人员数量,动态生成对应数量的Select下拉框组件。

formGroup = new FormGroup({});
  constructor(private fb: FormBuilder) {
  }
 createForm() : void{
    for(let i = 1; i <= this.members.length; i++) {
      this.formGroup.addControl(`${i}`, this.fb.control(null))
    }
  }

在Angular中,FormBuilder提供了简化FormControl、FormGroup及FormArray实例创建的语法糖,能显著减少构建复杂表单时的样板代码量。

class FormBuilder {
  group(controlsConfig: {...}, extra: {...}): FormGroup
  control(formState: any, validator?: ValidatorFn | ValidatorFn[] | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null): FormControl
  array(controlsConfig: any[], validator?: ValidatorFn | ValidatorFn[] | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null): FormArray
}

group方法:用于构建一个新的FormGroup实例,支持配置多个控件及其验证器。

this.form = this.fb.group({
  name: '123',
  age: 9,
  gender: false
})

control方法:用于构建一个新的FormControl实例,可指定初始状态及同步/异步验证器。

this.formGroup.addControl(`${i}`, this.fb.control(null))

array方法:用于构造一个新的FormArray实例,适用于动态增减的控件集合。

form: FormArray = new FormArray<any>([
    this.fb.control(null),
    this.fb.control(null),
    this.fb.control(null),
  ]);

数据绑定

<app-member-select [members]="users" formControlName="{{ i + 1 }}"></app-member-select>

MemberSelectComponent组件封装了具体的Select逻辑,负责接收人员列表并处理选择事件。

import {Component, forwardRef, Input, OnInit, Output} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from "@angular/forms";
import {User} from "../../../entity/user";
import {BehaviorSubject} from "rxjs";
import {UserService} from "../../../service/user.service";
import {EventEmitter} from "protractor";
import {Assert} from "@yunzhi/utils";
@Component({
  selector: 'app-member-select',
  templateUrl: './member-select.component.html',
  styleUrls: ['./member-select.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => MemberSelectComponent)
  }]
})
export class MemberSelectComponent implements OnInit, ControlValueAccessor {
  @Input()
  members: User[] = [];
  memberSelect = new FormControl<User>(null);
  ngOnInit(): void {
  }
  /**
   * 子组件需要向父组件弹值时,直接调用参数的fn方法
   * 相当于@Ouput()
   * @param fn 此类型取决于当前组件的弹出值类型
   */
  registerOnChange(fn: (user: User) => void): void {
    this.memberSelect.valueChanges.subscribe((data => {
      fn(data);
    }));
  }
  registerOnTouched(fn: any): void {
  }
  /**
   * 将FormControl中的值通过此方法写入
   * FormControl的值每变换一次,该方法被重新执行一次
   * 相当于@Input set XXX
   */
  writeValue(user: User): void {
    if (user === null) {
      return;
    }
    this.memberSelect.setValue(user);
  }
  /**
  * 比较函数,标识用哪个字段来比较两个对象是否为同一个对象
  * @param t1 源
  * @param t2 目标
  */
  compareFn(t1: { id: number }, t2: { id: number }): boolean {
    return t1 && t2 ? t1.id === t2.id : t1 === t2;
  }
}

通过订阅表单组的valueChanges事件,获取当前所有Select的选中值。遍历这些值,提取出非空的ID并加入待过滤数组,最后利用filter方法从原始人员列表中排除这些ID,实现选项的动态减少。

this.formGroup.valueChanges.subscribe(v => {
    let idsToFilter: number[] = [];
    // 遍历 v 的属性,如果属性值不为 null,则将其 id 加入数组
    for (const key in v) {
      if (v[key] !== null && v[key].id !== undefined) {
        idsToFilter.push(v[key].id);
      }
    }
    // 过滤掉与 idsToFilter 中任何一个 id 相同的元素
    this.users = this.members.filter(member => !idsToFilter.includes(member.id));
})

至此,目标效果已达成。希望本文关于动态减少Select Option选项的实现方法对您有所帮助,更多相关技术细节可关注本站其他文章。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章