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)
这种方式保留原表单的所有数据和状态,仅通过筛选后的列表控制显示,更安全且易维护。
- 添加筛选结果存储属性
在组件中声明一个数组,用于存储筛选后的表单控件:
filteredControls: AbstractControl[] = [];
- 初始化时同步全量数据
在_initForm方法末尾添加,将全量控件赋值给筛选数组:
private _initForm() { // ... 原代码不变 this.filteredControls = [...this.listAccessor.controls]; }
- 修正并完善搜索方法
更新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); }); }
- 修改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>
- 添加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
相关产品推荐
相关产品推荐

