Angular NgbTypeahead首次点击输入框无法显示完整列表问题
问题描述
使用Angular的ngbTypeahead组件时,输入字符能正常获取匹配的字符串列表,但存在以下问题:首次进入或刷新页面后,点击空输入框无法显示完整列表;只有点击页面其他区域后再返回点击输入框,才能展示全量数据。需要实现首次点击空输入框就显示完整列表的效果。
原代码片段
HTML模板
<ng-template #rt let-r="result" let-t="term"> <a> <span class="ml-1"> <ngb-highlight [result]="r" [term]="t"></ngb-highlight> </span> </a> </ng-template> <div class="form-group row mb-1"> <label for="libelle" class="col-md-3 text-right pr-0">Employeur</label> <div class="col-md-3"> <input class="form-control ml-1" id="libelle" name="libelle" [(ngModel)]="libelle" placeholder="Employeur" [ngbTypeahead]="search" (selectItem)="selectItemLibelle($event)" [inputFormatter]="formatMatches" [resultTemplate]="rt" (focus)="focus$.next($event.target.value)" > </div> </div>
TypeScript逻辑
search = (text$: Observable<string>) => { const debouncedText$ = text$.pipe(debounceTime(200), distinctUntilChanged() ); const inputFocus$ = this.focus$; return merge(debouncedText$, inputFocus$).pipe( mergeMap((term) => { return this.getEmp(term).pipe(map((emp: any) => { return ((!term || false || term === '') ? emp : emp.filter(v => v.toLowerCase().indexOf(term.toLowerCase()) > -1)); })); }) ); } selectItemLibelle($event) { this.libelle = $event.item; $event.preventDefault(); } formatMatches = (value: any) => value || '';
getEmp方法
getEmp(term): Observable<any[]> { this.fe2ModeleService.getEmployeurs(term).subscribe((employeurs) => {this.listEmp = employeurs;}); return of(this.listEmp);}
问题根源
- 异步请求处理错误:
getEmp方法中,先订阅HTTP请求再返回of(this.listEmp),但HTTP请求是异步的,首次调用时this.listEmp还未被赋值,导致返回空数组。 - 首次focus时数据未就绪:页面初始化后第一次点击输入框,
focus$触发时全量数据还没加载完成,所以没有列表显示;第二次点击时,之前的异步请求已完成,listEmp有值,因此能正常展示。
解决方案
1. 修复getEmp的异步逻辑
直接返回服务的Observable,避免同步返回未赋值的变量:
getEmp(term): Observable<any[]> { // 直接返回HTTP请求的Observable,同时可选通过tap缓存数据到listEmp return this.fe2ModeleService.getEmployeurs(term).pipe( tap(employeurs => this.listEmp = employeurs) ); }
2. 优化search方法的数据流
确保首次focus时能正确触发全量数据查询:
// 先在组件中定义focus$为Subject focus$ = new Subject<string>(); search = (text$: Observable<string>) => { const debouncedText$ = text$.pipe(debounceTime(200), distinctUntilChanged()); return merge(debouncedText$, this.focus$).pipe( mergeMap(term => { // 空term时传入空字符串请求全量数据 return this.getEmp(term || '').pipe( map(emp => { if (!term || term === '') { return emp; } else { // 用includes替代indexOf,逻辑更清晰 return emp.filter(v => v.toLowerCase().includes(term.toLowerCase())); } }) ); }) ); }
3. 可选:预加载全量数据
如果希望页面初始化时就加载好全量数据,避免首次点击的加载延迟,可在ngOnInit中预加载:
ngOnInit(): void { this.getEmp('').subscribe(); }
修改后效果
首次点击空输入框时,focus$会传入空字符串,触发getEmp('')获取全量数据,由于getEmp直接返回HTTP请求的Observable,能正确拿到数据并传递给ngbTypeahead,从而直接显示完整列表。
内容的提问来源于stack exchange,提问作者martin
相关产品推荐
相关产品推荐

