Spring WebFlux+Thymeleaf如何显示响应式集合的真实大小?
问题原因分析
你使用的IReactiveDataDriverContextVariable是Thymeleaf的响应式数据驱动渲染组件,设置的批次大小为1意味着Thymeleaf会从Flux中每次拉取1条数据进行渲染。此时#lists.size(items)获取的是当前批次的数据集大小(也就是你看到的1),而非整个Flux的总数据量。
解决方案
方案1:后端接口返回总条数(最优)
如果你的REST后端支持返回总数据量(比如分页接口会附带totalCount字段),可以直接在请求时获取总条数并传入Model:
控制器代码
@GetMapping("/counterparties") public String init(Model model) { // 假设后端返回包含总条数和数据的响应对象(自定义PageResponse类) Mono<PageResponse<Counterpart>> responseMono = webClient .get() .uri(uriBuilder -> uriBuilder.path("/findAllCounterpart").build()) .retrieve() .bodyToMono(new ParameterizedTypeReference<PageResponse<Counterpart>>() {}); // 拆分总条数和流式数据 Mono<Integer> totalCountMono = responseMono.map(PageResponse::getTotal); Flux<Counterpart> dataFlux = responseMono.flatMapIterable(PageResponse::getData); // 保持响应式流式渲染 IReactiveDataDriverContextVariable reactiveData = new ReactiveDataDriverContextVariable(dataFlux, 1); model.addAttribute("items", reactiveData); // 传入总条数 model.addAttribute("totalRows", totalCountMono); return "counterparties.html"; } // 自定义PageResponse示例 class PageResponse<T> { private int total; private List<T> data; // getter/setter }
模板代码
<label th:text="'total rows: ' + ${totalRows}">rows number in table</label>
方案2:收集Flux为List(放弃流式渲染)
如果后端不支持返回总条数,可以将Flux收集为Mono,一次性获取总条数,但会失去响应式流式渲染的优势:
控制器代码
@GetMapping("/counterparties") public String init(Model model) { Mono<List<Counterpart>> itemsMono = webClient .get() .uri(uriBuilder -> uriBuilder.path("/findAllCounterpart").build()) .retrieve() .bodyToFlux(Counterpart.class) .collectList(); model.addAttribute("items", itemsMono); // 直接从List获取大小 model.addAttribute("totalRows", itemsMono.map(List::size)); return "counterparties.html"; }
模板代码
<label th:text="'total rows: ' + ${totalRows}">rows number in table</label>
方案3:前端JS实时统计(保留流式渲染)
无需修改后端,通过JavaScript监听表格行的添加,实时统计总条数:
模板代码
<label id="totalRowsLabel">total rows: 0</label> <table> <!-- 原表格内容不变 --> <thead> <tr> <td>ID</td> <td>Name</td> <td>Alive</td> </tr> </thead> <tbody> <tr th:each="item : ${items}"> <td th:text="${item.counterpartID}" /> <td th:text="${item.counterpartName}" /> <td th:text="${item.alive}"/> </tr> </tbody> </table> <script> const tbody = document.querySelector('table tbody'); const totalLabel = document.getElementById('totalRowsLabel'); // 更新计数函数 const updateTotal = () => { const rowCount = tbody.querySelectorAll('tr').length; totalLabel.textContent = `total rows: ${rowCount}`; }; // 初始更新 updateTotal(); // 监听行的添加/移除 const observer = new MutationObserver(updateTotal); observer.observe(tbody, { childList: true }); </script>
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 方案1 | 保留流式渲染,总条数准确 | 依赖后端支持返回总条数 |
| 方案2 | 实现简单,总条数准确 | 数据量大时页面加载慢,失去响应式优势 |
| 方案3 | 无需后端修改,保留流式渲染 | 总条数随数据推送逐步更新,存在延迟 |
内容的提问来源于stack exchange,提问作者Oleg
相关产品推荐
相关产品推荐

