如何在ng2 smart table中实现服务端分页?(SpringBoot+Angular)
Hey,我之前刚好基于SpringBoot+Angular实现过ng2-smart-table的服务端分页,给你一步步拆解下具体怎么搞,应该能帮到你👇
实现ng2-smart-table服务端分页(SpringBoot + Angular)
一、Angular前端配置
1. 先配置表格基础参数
首先在你的组件里定义settings,核心是开启服务端分页、排序、过滤,同时设置默认每页条数:
settings = { pager: { display: true, perPage: 10 // 默认每页展示10条 }, serverPagination: true, // 关键:开启服务端分页 serverSorting: true, // 可选:开启服务端排序 serverFiltering: true, // 可选:开启服务端过滤 columns: { id: { title: 'ID' }, name: { title: '商品名称' }, price: { title: '价格' } // 其他业务列按需配置 } };
2. 自定义数据源(核心步骤)
ng2-smart-table默认的LocalDataSource是本地处理数据,所以我们要对接后端接口来获取分页数据。这里有两种方式:
方式一:用官方提供的ServerDataSource(简单快捷)
直接注入HttpClient,初始化ServerDataSource时指定后端接口和参数映射:
import { ServerDataSource } from 'ng2-smart-table'; import { HttpClient } from '@angular/common/http'; // 组件类里 dataSource: ServerDataSource; constructor(private http: HttpClient) { this.dataSource = new ServerDataSource(http, { endPoint: '/api/items', // 你的后端接口地址 pagerPageKey: 'page', // 后端接收当前页的参数名(SpringBoot默认是page) pagerLimitKey: 'size', // 后端接收每页条数的参数名(SpringBoot默认是size) sortFieldKey: 'sort', // 排序字段参数名(SpringBoot默认用sort,格式如name,asc) sortDirKey: '', // 因为SpringBoot的sort已经包含方向,这里留空即可 filterFieldKey: (fieldName) => fieldName // 过滤字段直接映射,比如前端过滤name,后端就接收name参数 }); }
方式二:自定义数据源(更灵活,适合复杂业务)
如果官方的ServerDataSource满足不了你的业务逻辑(比如需要额外参数、自定义请求头),可以自己监听表格的分页/排序/过滤事件,手动调用后端接口:
import { LocalDataSource } from 'ng2-smart-table'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Ng2SmartTableComponent } from 'ng2-smart-table'; import { ViewChild, OnInit } from '@angular/core'; // 组件类里 dataSource = new LocalDataSource(); table: Ng2SmartTableComponent; // 用ViewChild获取表格实例 @ViewChild(Ng2SmartTableComponent) set ng2SmartTable(table: Ng2SmartTableComponent) { if (table) { this.table = table; // 监听表格的变更事件(分页、排序、过滤) this.table.onChanged().subscribe((change) => { if (['page', 'sort', 'filter'].includes(change.action)) { this.loadData( change.pager.page, change.pager.perPage, change.sort, change.filter ); } }); } } constructor(private http: HttpClient) {} ngOnInit() { // 初始加载第一页数据 this.loadData(1, 10); } // 自定义加载数据的方法 loadData(page: number, perPage: number, sort: any = {}, filter: any = {}) { // SpringBoot的Pageable默认page从0开始,所以前端的page要减1 const params = new HttpParams() .set('page', (page - 1).toString()) .set('size', perPage.toString()) // 处理排序:如果有排序字段,拼接成SpringBoot需要的格式(比如name,asc) .set('sort', sort.field ? `${sort.field},${sort.direction}` : '') // 处理过滤:比如前端过滤name,就传递name参数 .set('name', filter.name || ''); this.http.get<any>('/api/items', { params }).subscribe(res => { // 后端返回的结构一般是{ content: [...], totalElements: 总条数 } this.dataSource.load(res.content, res.totalElements, page); }); }
最后在模板里绑定表格:
<ng2-smart-table [settings]="settings" [source]="dataSource" ></ng2-smart-table>
二、SpringBoot后端实现
1. 编写分页接口
SpringBoot可以直接用Pageable接收分页参数,返回Page对象(Page自带content和totalElements,刚好匹配前端需要的结构):
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.CrossOrigin; @RestController @RequestMapping("/api/items") @CrossOrigin // 解决跨域问题,如果前后端端口不同的话 public class ItemController { private final ItemService itemService; // 构造注入 public ItemController(ItemService itemService) { this.itemService = itemService; } @GetMapping public Page<Item> getItems( @RequestParam(required = false) String name, // 过滤参数,按需添加 Pageable pageable) { // 调用业务层处理查询 if (name == null || name.isBlank()) { return itemService.findAll(pageable); } return itemService.findByNameContaining(name, pageable); } }
2. 业务层和DAO层
Service层调用Repository,Repository继承JpaRepository即可支持分页:
// Service层 import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service public class ItemService { private final ItemRepository itemRepository; public ItemService(ItemRepository itemRepository) { this.itemRepository = itemRepository; } public Page<Item> findAll(Pageable pageable) { return itemRepository.findAll(pageable); } public Page<Item> findByNameContaining(String name, Pageable pageable) { return itemRepository.findByNameContainingIgnoreCase(name, pageable); } } // DAO层 import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface ItemRepository extends JpaRepository<Item, Long> { // 支持模糊查询+分页 Page<Item> findByNameContainingIgnoreCase(String name, Pageable pageable); }
三、常见坑点排查
- 分页不生效:检查前端是否开启了
serverPagination: true,后端返回的totalElements是否正确(ng2-smart-table需要总条数来计算分页页数)。 - page起始值问题:SpringBoot的
Pageable默认page从0开始,而ng2-smart-table的分页默认从1开始,所以前端传递参数时要减1(比如刚才代码里的(page - 1).toString())。 - 排序/过滤失效:检查前后端的参数名是否匹配,比如前端传递的排序参数格式要和后端
Pageable的sort参数一致(格式为字段名,方向,比如name,asc)。
这样一套下来就能实现完整的服务端分页了,我当时踩过参数名不匹配和page起始值的坑,注意这两点基本就没问题啦~
内容的提问来源于stack exchange,提问作者Venkatesh R




