You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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自带contenttotalElements,刚好匹配前端需要的结构):

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())。
  • 排序/过滤失效:检查前后端的参数名是否匹配,比如前端传递的排序参数格式要和后端Pageablesort参数一致(格式为字段名,方向,比如name,asc)。

这样一套下来就能实现完整的服务端分页了,我当时踩过参数名不匹配和page起始值的坑,注意这两点基本就没问题啦~

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

火山引擎 最新活动