Angular绑定nz-autocomplete的ScrollEndDirective滚动事件不触发排查
解决Ng-Zorro下拉框滚动触底无响应问题及替代方案
一、Nz-Autocomplete滚动指令修复方案
问题原因
你的自定义指令绑定在nz-autocomplete组件本身,但实际可滚动的是组件内部的下拉菜单容器,而非nz-autocomplete元素本身,所以@HostListener监听的scroll事件不会触发。
修改后的指令代码
import { Directive, ElementRef, EventEmitter, Output, AfterViewInit, Renderer2, OnDestroy } from '@angular/core'; @Directive({ selector: '[scrollEnd]' }) export class ScrollEndDirective implements AfterViewInit, OnDestroy { @Output() scrolledToEnd = new EventEmitter<boolean>(); private scrollContainer: HTMLElement | null = null; private scrollListener: (() => void) | null = null; constructor(private el: ElementRef, private renderer: Renderer2) { console.log('constructor logger'); } ngAfterViewInit(): void { // 定位nz-autocomplete内部的滚动容器(Ng-Zorro默认类名为ant-autocomplete-dropdown-menu) this.scrollContainer = this.el.nativeElement.querySelector('.ant-autocomplete-dropdown-menu'); if (this.scrollContainer) { // 绑定滚动事件监听 this.scrollListener = this.renderer.listen(this.scrollContainer, 'scroll', () => this.onScroll()); } } onScroll(): void { if (!this.scrollContainer) return; console.log(this.scrollContainer); // 兼容像素精度问题,判断滚动是否触底 const isAtEnd = this.scrollContainer.scrollHeight - this.scrollContainer.scrollTop <= this.scrollContainer.clientHeight + 1; this.scrolledToEnd.emit(isAtEnd); } ngOnDestroy(): void { // 销毁监听避免内存泄漏 if (this.scrollListener) { this.scrollListener(); } } }
说明
- 利用
AfterViewInit钩子在视图初始化后,查找内部滚动容器元素 - 使用
Renderer2绑定滚动事件,替代@HostListener - 增加
OnDestroy钩子销毁监听,防止内存泄漏 - 滚动触底判断增加1px的容错,避免因浏览器像素精度问题导致判断失效
组件用法保持不变,仍将指令绑定在nz-autocomplete上即可。
二、Nz-Select替代方案(更简洁)
如果你愿意切换到Nz-Select,可通过以下配置满足你的需求:
核心配置解决你的痛点
- 避免空输入显示NotFoundContent:自定义
nzNotFoundContent模板,仅在有搜索输入且无结果时显示 - 选中后可清除输入文本:使用
nzMode="combobox"模式,允许选中后编辑输入框内容,搭配nzAllowClear支持清除
示例代码
import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Subject } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Component({ selector: 'nz-demo-select-infinite', template: ` <nz-select nzShowSearch nzMode="combobox" nzAllowClear [(ngModel)]="selectedValue" (scrollToBottom)="onScrollEnd()" [nzNotFoundContent]="notFoundTpl" (nzSearchChange)="onSearch($event)" style="width: 200px;" > <nz-option *ngFor="let option of options" [nzValue]="option.name" [nzLabel]="option.name"></nz-option> <ng-template #notFoundTpl> <ng-container *ngIf="searchValue && options.length === 0">无匹配结果</ng-container> </ng-template> </nz-select> `, }) export class NzDemoSelectInfiniteComponent { selectedValue?: string; options: any[] = []; searchValue = ''; private searchSubject = new Subject<string>(); constructor(private http: HttpClient) { this.searchSubject .pipe(switchMap((value) => this.search(value))) .subscribe((options) => { this.options = options; }); } onSearch(value: string): void { this.searchValue = value; this.searchSubject.next(value); } search(value: string) { return this.http .get<any[]>('https://jsonplaceholder.typicode.com/users') .pipe(map((response) => response)); } onScrollEnd(): void { console.log('Scrollbar is at the end'); // 在此实现加载更多逻辑 } }
说明
- Nz-Select内置
scrollToBottom事件,无需自定义指令即可实现滚动触底监听 nzMode="combobox":开启可编辑模式,选中后输入框仍可编辑,支持手动清除内容- 自定义
nzNotFoundContent:通过条件判断,仅在用户输入搜索内容且无匹配结果时显示提示
内容的提问来源于stack exchange,提问作者Welp
相关产品推荐
相关产品推荐

