- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
导读
本文基于 ng-zorro-antd 官方 Demo「下拉加载 / Load Data on Scroll」展开,讲解如何使用nz-select的下拉滚动事件(nzScrollToBottom)与自定义下拉渲染模板nzDropdownRender实现滚动到底部自动加载远程数据(分页式无限滚动)。你将掌握从模板绑定、信号状态管理到底层虚拟滚动触发原理的完整链路,并了解服务端搜索nzServerSearch等相关 API 的配合用法,可直接迁移到真实业务的分页选择器场景。
场景与示例背景
下拉加载(Scroll Load)适用于数据量庞大、无法一次性渲染的远程选项场景,例如用户列表、成员选择、省市联动等。其核心交互是:下拉面板打开后先展示首屏数据,用户滚动到列表底部时自动发起下一次请求,追加新数据并渲染。
官方示例 scroll-load.md 对应的是演示页「下拉加载」,对应的完整实现为 scroll-load.ts,它通过 randomuser.me 这类公开的随机用户接口模拟远程分页数据源(注:示例代码中引用了该外部接口,实际业务中请替换为自有服务端接口)。注意示例中接口数据基于 mock 接口,本身会因网络环境变化,工程上通常会在服务端实现真正的分页参数。
完整示例代码与逐段解析
1. 模板部分:事件绑定与自定义加载指示
示例 scroll-load.ts 的组件模板如下:
<nz-select [nzOptions]="options()" (nzScrollToBottom)="loadMore()" nzPlaceHolder="Select users" nzAllowClear [nzDropdownRender]="renderTemplate" /> <ng-template #renderTemplate> @if (loading()) { <nz-spin /> } </ng-template>关键点逐个说明:
[nzOptions]="options()":以数组形式直接向nz-select传入选项列表,取代逐个写nz-option子组件。官方 API 文档 index.zh-CN.md 中对nzOptions的定义为Array<{ label: string | number | TemplateRef<any>; value: any; key?: string | number; disabled?: boolean; hide?: boolean; groupLabel?: string | TemplateRef<any>; }>。由于选项是异步追加的,用数组绑定比静态nz-option更贴合动态加载场景。(nzScrollToBottom)="loadMore()":下拉列表滚动到底部时的回调事件,类型为EventEmitter<any>(见文档 API 表)。此处绑定到loadMore(),即"滚到底 → 加载下一页"。nzPlaceHolder="Select users":占位提示文案。nzAllowClear:允许一键清空已选项。在 select.component.ts 中其声明为@Input({ transform: booleanAttribute }) nzAllowClear = false,即默认关闭,写成属性形式即开启;渲染逻辑见组件模板@if (nzAllowClear && !nzDisabled && listOfValue.length)下的清空按钮分支。[nzDropdownRender]="renderTemplate":自定义下拉菜单渲染模板。当加载进行中时,模板内渲染nz-spin加载指示器。该模板会被渲染在下拉选项列表的底部,其挂载位置见下文源码分析。
2. 组件类部分:信号状态与异步加载
示例组件类 scroll-load.ts 使用 Angular 现代信号(signal)API 管理状态:
export class NzDemoSelectScrollLoadComponent implements OnInit { private readonly http = inject(HttpClient); readonly options = signal<Array<{ label: string; value: string }>>([]); readonly loading = signal(false); ngOnInit(): void { this.loadMore(); } getRandomNameList(): Observable<string[]> { return this.http.get<{ results: MockUser[] }>('https://api.randomuser.me/?results=10').pipe( map(res => res.results.map(item => item.name.first)), catchError(() => of<string[]>([])) ); } loadMore(): void { this.loading.set(true); this.getRandomNameList().subscribe(data => { this.loading.set(false); this.options.update(options => [...options, ...data.map(item => ({ label: item, value: item }))]); }); } }逻辑链路:
ngOnInit中调用一次loadMore(),保证首屏即有数据,避免下拉面板打开时为空列表;getRandomNameList()发起 HTTP 请求(每次取 10 条),map提取用户名,catchError兜底返回空数组,避免网络异常导致订阅中断;loadMore()先置loading = true(触发下拉底部nz-spin显示),请求返回后置loading = false,并通过options.update将新数据追加(concat)到已有数组尾部——注意是追加而非替换,这正是分页加载的核心;- 每项被规范为
{ label, value }结构,供nzOptions消费。
模板样式(nz-select { width: 100%; })让选择器撑满容器宽度,便于观察滚动行为。
关键 API 详解
以下 API 全部出自 index.zh-CN.md 与 index.en-US.md 的 Select 官方文档表:
| API | 说明 | 类型 | 默认值 |
|---|---|---|---|
(nzScrollToBottom) | 下拉列表滚动到底部的回调 | EventEmitter<any> | - |
[nzOptions] | option 列表,可取代nz-option | Array<{ label; value; key?; disabled?; hide?; groupLabel? }> | - |
[nzDropdownRender] | 自定义下拉菜单内容(渲染在选项列表底部) | TemplateRef | - |
[nzDropdownStyle] | 下拉菜单的 style 属性 | object | - |
[nzDropdownClassName] | 下拉菜单的 className 属性 | string \| string[] | - |
[nzDropdownMatchSelectWidth] | 下拉菜单与选择器同宽 | boolean | true |
[nzOptionHeightPx] | 下拉菜单中每个 Option 的高度 | number | 32 |
[nzServerSearch] | 服务端搜索开关,为true时不再在前端过滤nz-option | boolean | false |
[nzAllowClear] | 是否允许一键清空 | boolean | false |
[nzPlaceHolder] | 占位提示 | string \| TemplateRef | - |
在远程数据 + 滚动加载场景中,nzServerSearch特别值得注意:开启后组件不会再对已有选项做本地filter(源码中见 select.component.ts 附近if (!this.nzServerSearch && this.searchValue)的过滤分支),搜索关键词需自行通过(nzOnSearch)发给服务端——这正是"服务端分页 + 服务端搜索"的完整形态。若同时配合nzOptionHeightPx与nzDropdownMatchSelectWidth,可微调下拉性能与宽度表现。
底层实现原理:从滚动事件到加载回调
nzScrollToBottom并不是简单监听原生scroll事件,而是建立在CDK 虚拟滚动(cdk-virtual-scroll-viewport)之上的阈值检测。其调用链如下:
- select.component.ts 中,
nz-option-container的(scrollToBottom)事件被透传为nzScrollToBottom.emit(),即对外暴露的@Output() readonly nzScrollToBottom = new EventEmitter<void>()(见 select.component.ts); - 真正的滚动检测在 option-container.component.ts 的
onScrolledIndexChange(index)中完成:
const isAtBottom = this.listOfContainerItem.length - index <= this.maxItemLength + 1; const wasAtBottom = this.listOfContainerItem.length - this.scrolledIndex <= this.maxItemLength + 1; if (isAtBottom && !wasAtBottom) { this.scrollToBottom.emit(); }源码注释点明了设计动机:CDK 的scrolledIndexChange内部有 debounce,快速滚动时可能跳过若干索引,因此通过"当前是否在底部、之前是否在底部"的状态比较,只在跨越底部阈值的那一刻触发一次事件,避免重复触发加载。maxItemLength默认对应nzOptionOverflowSize(可见最大选项数,默认与下拉最大高度相关),itemSize对应nzOptionHeightPx(默认 32),虚拟滚动的缓冲区间maxBufferPx / minBufferPx也由二者相乘得出(见 option-container.component.ts)。
nzDropdownRender模板则被挂载在虚拟滚动视口之后:option-container.component.ts 中<ng-template [ngTemplateOutlet]="dropdownRender" />,所以加载指示器天然出现在选项列表末尾——滚动到最底部时正好能看到"正在加载"。
这条链路意味着:只要选项列表使用nzOptions动态追加,虚拟滚动会自动重算总高度,滚动到底即触发回调,无需手动计算滚动位置。
与自定义下拉菜单示例的呼应
同目录下的 custom-dropdown-menu.ts 展示了nzDropdownRender的另一典型用法:在选项列表底部追加一个"添加自定义项"的分隔线 + 输入框:
<nz-select nzShowSearch nzAllowClear [nzDropdownRender]="renderTemplate" nzPlaceHolder="custom dropdown render"> ... </nz-select> <ng-template #renderTemplate> <nz-divider /> <div class="container"> <input type="text" nz-input #inputElement /> <a class="add-item" (click)="addItem(inputElement)">...</a> </div> </ng-template>由此可以看出nzDropdownRender的本质是下拉面板的"尾部插槽":既可以渲染加载指示器(滚动加载场景),也可以渲染自定义操作区(快速新增场景),甚至可以组合两者——底部先放加载提示、再放"加载更多"按钮。
测试层面的验证
仓库测试 select.spec.ts 中有专门针对nzDropdownRender的用例should nzDropdownRender work:打开下拉时自定义模板默认不出现,注入dropdownTemplate后,下拉面板内出现.dropdown-render节点且内容为dropdownRender,验证了模板确实被渲染进下拉容器。测试组件模板定义在 select.spec.ts 附近。这些用例可作为"自定义下拉内容是否生效"的回归参考。
工程实践建议
- 分页参数化:示例中的接口没有传递页码,生产环境应在
loadMore()中维护page/pageSize状态,并在请求 URL 或 body 中携带,服务端返回后按total判断是否还有下一页,无更多数据时不再继续触发请求(避免滚动到底反复请求空页)。 - 防抖与竞态:
nzScrollToBottom底层已做跨底阈值去重,但网络请求本身仍可能并发;可在组件中维护loading()状态(如示例所示)作为互斥锁,loading为真时忽略后续滚动触发。 - 错误处理:参考示例的
catchError(() => of([]))兜底,同时建议补充错误提示与"重试"入口。 - 服务端搜索组合:若同时需要搜索,开启
nzServerSearch后通过(nzOnSearch)将关键词提交服务端,与滚动分页共用同一套分页状态。 - 性能:虚拟滚动已保证只渲染可视区域内的选项(
cdkVirtualFor),大数据量下无需额外处理;如选项高度与默认 32px 不符,务必同步设置nzOptionHeightPx,否则虚拟滚动定位会偏差。
小结
本文围绕官方 Demo「下拉加载」完整梳理了 ng-zorro-antdnz-select的远程滚动加载方案:模板侧用(nzScrollToBottom)触发、[nzDropdownRender]展示加载态、[nzOptions]动态追加数据;源码侧则揭示了 CDK 虚拟滚动 + 跨底阈值检测的触发机制。掌握这套模式后,你可以在成员选择、数据字典、级联数据等任何"选项数量不确定且可能很大"的业务中,快速实现体验流畅的分页式下拉选择器。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
ng-zorro-antd Button 加载中状态(nzLoading)完整实战指南
ng zorro antd Button 加载中状态(nzLoading)完整实战指南 导读 在 Angular 应用中使用 ng zorro antd 的 B
UI组件前端ng-zorro-antd Cascader 下拉菜单自由扩展:`nzPopupRender` 完整实战指南
ng zorro antd Cascader 下拉菜单自由扩展: nzPopupRender 完整实战指南 nzPopupRender 是 ng zorro a
UI组件前端ng-zorro-antd Mention 异步加载实战:用 `(nzOnSearchChange)` 实现远程搜索建议
ng zorro antd Mention 异步加载实战:用 nzOnSearchChange 实现远程搜索建议 当匹配内容列表需要异步返回时(例如从远程接口拉
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考