如何基于API数据实现ng-bootstrap表格的排序、搜索过滤与分页?
解决Ng-bootstrap表格排序、过滤、分页适配动态API数据的问题
我完全懂你遇到的痛点——官方示例用的是现成的静态数据数组,直接套用到异步加载的API数据上肯定会踩坑,毕竟数据不是一开始就存在的。咱们一步步调整代码,把排序、搜索过滤和分页功能都稳稳装上:
第一步:先把必要的Ng-bootstrap模块导入到位
首先,在你的模块文件(比如AdminModule或者AppModule)里,得把表格相关的功能模块加进去,不然指令会失效:
import { NgbSortModule, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ...其他你已经导入的模块 NgbSortModule, NgbPaginationModule ] }) export class AdminModule { }
第二步:修改Component代码,适配异步数据+实现功能逻辑
咱们要给TS代码加上排序、过滤、分页的变量和方法,同时处理好API数据的异步加载时机:
import { Component, OnInit, ViewChild } from '@angular/core'; import { NgxSpinnerService } from 'ngx-spinner'; import { TransactionsService } from '../transactions.service'; import { Transactions, Career, Faculty, RoleType } from 'src/assets/models'; import { RegisterService } from '../register.service'; import { AlertService } from '../alert.service'; import { NgbSort, NgbSortEvent } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'app-admin', templateUrl: './admin.component.html', styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { content: Transactions; // 新增:用于过滤、分页的中间数据变量 filteredTransactions: any[] = []; currentPageTransactions: any[] = []; searchText = ''; page = 1; pageSize = 10; totalItems = 0; analytic = false; uniFaculties: Faculty; uniCareers: Career; uniUsers: RoleType; // 获取排序组件的DOM引用,用来绑定排序事件 @ViewChild(NgbSort) sort: NgbSort; constructor( private modalService: NgbModal, private spinner: NgxSpinnerService, private alertService: AlertService, private transactionService: TransactionsService ) { } ngOnInit() { this.spinner.show(); this.transactionService.getAllTransactions() .subscribe(res => { this.content = res; // 数据加载完成后,初始化过滤数组(用副本避免修改原数据) this.filteredTransactions = [...this.content.transactions]; this.totalItems = this.filteredTransactions.length; // 初始化当前页展示的数据 this.updateCurrentPageData(); console.log(this.content); // 测试用的日志 this.spinner.hide(); // 把加载动画隐藏移到这里,确保数据加载完再隐藏 }); } // 搜索过滤方法:根据输入框内容过滤交易数据 onSearch() { if (!this.searchText.trim()) { this.filteredTransactions = [...this.content.transactions]; } else { const searchTerm = this.searchText.toLowerCase(); this.filteredTransactions = this.content.transactions.filter(transaction => transaction.trx_ID.toString().includes(searchTerm) || transaction.user_ID.toString().includes(searchTerm) || transaction.trx_type.toLowerCase().includes(searchTerm) || transaction.description.toLowerCase().includes(searchTerm) || transaction.amount.toString().includes(searchTerm) ); } this.totalItems = this.filteredTransactions.length; this.page = 1; // 搜索后自动回到第一页 this.updateCurrentPageData(); } // 排序处理方法:根据表格头的排序指令处理数据 onSort(event: NgbSortEvent) { if (!event.active || event.direction === '') { this.filteredTransactions = [...this.content.transactions]; this.updateCurrentPageData(); return; } this.filteredTransactions.sort((a, b) => { const isAsc = event.direction === 'asc'; switch (event.active) { case 'trxId': return compare(a.trx_ID, b.trx_ID, isAsc); case 'userId': return compare(a.user_ID, b.user_ID, isAsc); case 'type': return compare(a.trx_type, b.trx_type, isAsc); case 'date': return compare(new Date(a.date), new Date(b.date), isAsc); case 'description': return compare(a.description, b.description, isAsc); case 'amount': return compare(a.amount, b.amount, isAsc); default: return 0; } }); this.updateCurrentPageData(); } // 更新当前页要展示的数据 updateCurrentPageData() { const startIndex = (this.page - 1) * this.pageSize; this.currentPageTransactions = this.filteredTransactions.slice(startIndex, startIndex + this.pageSize); } // 分页切换事件 onPageChange(page: number) { this.page = page; this.updateCurrentPageData(); } } // 辅助排序的工具函数,统一处理不同类型数据的排序逻辑 function compare(a: number | string | Date, b: number | string | Date, isAsc: boolean) { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); }
第三步:修改HTML模板,添加搜索框、排序指令和分页组件
把原来的表格模板调整一下,把Ng-bootstrap的指令、搜索输入框和分页组件加进去:
<div class="fluid-container admin-transactions" *ngIf="content"> <h1><span class="badge badge-info header-transactions"><i class="fas fa-exchange-alt"></i> Transacciones</span></h1> <!-- 搜索输入框 --> <div class="mb-3"> <input type="text" class="form-control" placeholder="Buscar transacción..." [(ngModel)]="searchText" (input)="onSearch()" > </div> <!-- 绑定排序指令的表格 --> <table class="table table-hover fixed_header" ngbSort (sortChange)="onSort($event)"> <thead class="thead table-info"> <tr class="row table-info"> <th class="col-1 d-flex justify-content-center" scope="col" ngbSortHeader="trxId" >ID de Transacción</th> <th class="col-1 d-flex justify-content-center" scope="col" ngbSortHeader="userId" >ID de Usuario</th> <th class="col-1 d-flex justify-content-center" scope="col" ngbSortHeader="type" >Tipo</th> <th class="col-4 d-flex justify-content-center" scope="col" ngbSortHeader="date" >Fecha</th> <th class="col-3 d-flex justify-content-center" scope="col" ngbSortHeader="description" >Descripción</th> <th class="col-2 d-flex justify-content-center" scope="col" ngbSortHeader="amount" >Monto</th> </tr> </thead> <tbody> <!-- 遍历当前页的数据,而不是原数据 --> <tr class="row" *ngFor='let row of currentPageTransactions'> <td class="col-1 d-flex justify-content-center id">{{ row.trx_ID }}</td> <td class="col-1 d-flex justify-content-center id">{{ row.user_ID }}</td> <td class="col-1 d-flex justify-content-center trx" [ngClass]="{'custom-class': row.trx_type === 'RCH' && row.amount !== 0, 'default-color': row.trx_type !== 'RCH'}">{{ row.trx_type}}</td> <td class="col-4 d-flex justify-content-center date">{{ row.date | date:'long' }} </td> <td class="col-3 d-flex justify-content-center description">{{ row.description }}</td> <td class="col-2 d-flex justify-content-center amount" [ngClass]="{'rch-class': row.trx_type === 'RCH' && row.amount !== 0 , 'pay-class': row.trx_type !== 'RCH'}">{{ row.amount | currency }}</td> </tr> </tbody> </table> <!-- 分页组件 --> <div class="d-flex justify-content-center mt-3"> <ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="totalItems" (pageChange)="onPageChange($event)" [maxSize]="5" [rotate]="true" [ellipses]="true" ></ngb-pagination> </div> </div> <hr>
几个关键细节提醒
- 异步数据时机:把
spinner.hide()移到subscribe回调里,确保只有当API数据加载完成后才隐藏加载动画,避免数据还没回来就渲染表格导致的空数据问题。 - 数据副本:处理过滤和排序时用的是原数据的副本(
[...this.content.transactions]),这样不会修改API返回的原始数据,避免后续逻辑出错。 - 排序字段映射:
ngbSortHeader的值要和onSort方法里的switch分支对应上,确保每一列的排序逻辑都能正确触发。
内容的提问来源于stack exchange,提问作者Luis Yao
相关产品推荐
相关产品推荐

