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

Angular如何按索引筛选FormArray并更新表单显示

问题:Angular响应式表单FormArray筛选显示

我有一个通过访问器引用的响应式表单,希望根据输入框的搜索词匹配对应索引,筛选FormArray并让页面上遍历响应式表单的*ngFor仅显示筛选后的结果。

现有代码

TS文件

//accessors
get listAccessor() {
    return this.listGroupForm.get('listContainer') as FormArray
}

private _initForm() {
    this.listGroupForm = this._formBuilder.group({
        listContainer: this._formBuilder.array((this._listData || []).map((item:ListTypes)=> {
            return this._formBuilder.group({
                id: [this._userID],
                list_id: [item.list_id],
                title: [{value: item.title, disabled: true},Validators.required],
            })
        }))
    })
}

// 已编写的搜索方法
public searchLists(search: string) {  
        return this.listAccessor.controls.filter((item,index) => {
            if (item.get('title')?.value === search) {
                console.log(item,index)
            }
        })
    }

HTML文件

<div class="flex flex-wrap pt-5 justify-center desk:flex-row desk:pt-7 desk:items-center">
    <div class="flex w-full desk:w-auto">
        <input mat-input type="text" class="flex border rounded p-2" #search placeholder="Search Lists" (input)="formatSearch($event)" (keydown.enter)="searchLists(search.value)"/>   
    </div>
    <div class="flex w-full desk:w-auto">
        <button mat-button (click)="searchLists(search.value)" class="w-full mt-5 ml-0 desk:mt-0 desk:w-auto desk:ml-5"><div class="text-black border p-2 rounded border-black hover:bg-gray-400 dark:text-gray-400 dark:border-gray-400 dark:hover:bg-blue-950">Search</div></button> 
    </div>
    <div class="flex flex-wrap pt-1 desk:flex-row desk:pt-7 pb-7 desk:items-center">
        <button mat-button class="w-full mt-5 ml-0 desk:mt-0 desk:w-auto">
            <div (click)="addList()" class="flex items-center text-black border p-2 rounded border-black hover:bg-gray-400 dark:text-gray-400 dark:border-gray-400 dark:hover:bg-blue-950">
                <mat-icon>create_new_folder</mat-icon>New List
            </div>
        </button>
    </div>   
</div>
<form [formGroup]="listGroupForm" class="pb-4 overflow-y-scroll overflow-x-hidden" style="max-height:50vh;" cdkDropList (cdkDropListDropped)="drop($event)">
    <ng-container formArrayName="listContainer">
        <div *ngFor="let data of listAccessor.controls; let i = index" class="pb-6" cdkDrag>
            <div [formGroupName]="i" class="flex-col pr-2">
                <div class="flex flex-wrap w-full desk:flex-row desk:items-center border rounded bg-gray-300 border-black dark:border-gray-400 dark:bg-black dark:text-gray-400">
                    <div class="w-full">
                        <div class="flex items-center w-full  bg-gray-400 dark:bg-blue-950">
                            <div class="flex w-full pt-4 pl-2 desk:pl-4 pb-4 text-xl items-center">
                                <input mat-input (input)="formatInputName(i,null,$event)" formControlName="title" type="text" class="title text-black bg-gray-400 dark:text-gray-400 dark:bg-blue-950" placeholder="Enter List Name"/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </ng-container>
</form>            

解决方案

推荐方案:维护筛选后的控件列表(不修改原FormArray)

这种方式保留原表单的所有数据和状态,仅通过筛选后的列表控制显示,更安全且易维护。

  1. 添加筛选结果存储属性
    在组件中声明一个数组,用于存储筛选后的表单控件:
filteredControls: AbstractControl[] = [];
  1. 初始化时同步全量数据
    在_initForm方法末尾添加,将全量控件赋值给筛选数组:
private _initForm() {
    // ... 原代码不变
    this.filteredControls = [...this.listAccessor.controls];
}
  1. 修正并完善搜索方法
    更新searchLists方法,实现模糊匹配(不区分大小写)并更新筛选数组:
public searchLists(search: string) {  
    const searchTerm = search.trim().toLowerCase();
    if (!searchTerm) {
        // 搜索词为空时显示全部
        this.filteredControls = [...this.listAccessor.controls];
        return;
    }
    // 按title模糊匹配筛选
    this.filteredControls = this.listAccessor.controls.filter(item => {
        const itemTitle = item.get('title')?.value?.toLowerCase() || '';
        return itemTitle.includes(searchTerm);
    });
}
  1. 修改HTML遍历逻辑
    将*ngFor的遍历对象改为filteredControls,同时通过原FormArray获取控件的索引以绑定formGroupName:
<ng-container formArrayName="listContainer">
    <!-- 使用trackBy避免不必要的DOM重渲染 -->
    <div *ngFor="let data of filteredControls; trackBy: trackByControl; let i = index" class="pb-6" cdkDrag>
        <!-- 通过原数组获取当前控件的索引,确保formGroupName绑定正确 -->
        <ng-container [formGroupName]="listAccessor.controls.indexOf(data)">
            <!-- 原内容保持不变 -->
            <div class="flex-col pr-2">
                <div class="flex flex-wrap w-full desk:flex-row desk:items-center border rounded bg-gray-300 border-black dark:border-gray-400 dark:bg-black dark:text-gray-400">
                    <div class="w-full">
                        <div class="flex items-center w-full  bg-gray-400 dark:bg-blue-950">
                            <div class="flex w-full pt-4 pl-2 desk:pl-4 pb-4 text-xl items-center">
                                <input mat-input (input)="formatInputName(i,null,$event)" formControlName="title" type="text" class="title text-black bg-gray-400 dark:text-gray-400 dark:bg-blue-950" placeholder="Enter List Name"/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </ng-container>
    </div>
</ng-container>
  1. 添加trackBy方法(可选但推荐)
    在组件中添加trackByControl方法,提升列表渲染性能:
trackByControl(index: number, control: AbstractControl): string | number {
    // 使用list_id作为唯一标识,没有则用索引
    return control.get('list_id')?.value || index;
}

备选方案:直接修改FormArray(不推荐)

如果必须通过修改FormArray实现筛选,需要先备份原控件,避免数据丢失:

private originalControls: AbstractControl[] = [];

private _initForm() {
    // ... 原代码不变
    this.originalControls = [...this.listAccessor.controls];
}

public searchLists(search: string) {  
    const searchTerm = search.trim().toLowerCase();
    // 清空当前FormArray
    while (this.listAccessor.length > 0) {
        this.listAccessor.removeAt(0);
    }
    if (!searchTerm) {
        // 恢复原控件
        this.originalControls.forEach(ctrl => this.listAccessor.push(ctrl));
        return;
    }
    // 添加筛选后的控件
    const filtered = this.originalControls.filter(item => {
        const itemTitle = item.get('title')?.value?.toLowerCase() || '';
        return itemTitle.includes(searchTerm);
    });
    filtered.forEach(ctrl => this.listAccessor.push(ctrl));
}

注意:此方案会修改原FormArray的结构和顺序,若有拖拽排序等功能,需要同步更新originalControls,否则会导致数据不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:22:09