Angular Bootstrap 多Typeahead组件弹窗闪烁问题排查
问题根源分析与解决方案
嘿,我一眼就发现问题所在了——你把两个Typeahead组件的核心资源都共享了,这直接导致它们互相干扰,出现弹窗闪烁的问题!具体来说有两个关键问题:
1. 共享模板引用变量导致实例判断错误
你给两个输入框都用了同一个模板引用变量#instance,Angular的@ViewChild('instance')只会获取到页面中第一个匹配的组件实例。当第二个组件触发事件时,代码里判断的!this.instance.isPopupOpen()其实是在检查第一个组件的弹窗状态,完全不符合预期。
2. 共享事件流导致跨组件触发
两个组件绑定了同一个focus$和click$ Subject,当你聚焦其中一个输入框时,事件会同时流入两个组件的搜索流,导致另一个组件的弹窗被意外触发,随后又因为错误的状态判断立刻关闭,这就是你看到的闪烁现象。
修改后的完整代码
.ts 文件
import {Component, ViewChild} from '@angular/core'; import {NgbTypeahead} from '@ng-bootstrap/ng-bootstrap'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/merge'; import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged'; const country = ["United States", "China", "Japan", "Germany"]; const nationality = ['American', 'Indian', 'Japanese', 'Chinese','Spanish']; @Component({ selector: 'app-accommodation', templateUrl: './accommodation.component.html', styleUrls: ['./accommodation.component.css'] }) export class AccommodationComponent { model: any; model1: any; // 为每个组件创建独立的实例引用 @ViewChild('countryInstance') countryInstance: NgbTypeahead; @ViewChild('nationalityInstance') nationalityInstance: NgbTypeahead; // 为每个组件创建独立的事件流 countryFocus$ = new Subject<string>(); countryClick$ = new Subject<string>(); nationalityFocus$ = new Subject<string>(); nationalityClick$ = new Subject<string>(); searchCountry = (text$: Observable<string>) => text$ .debounceTime(200).distinctUntilChanged() .merge(this.countryFocus$) .merge(this.countryClick$.filter(() => !this.countryInstance.isPopupOpen())) .map(term => (term === '' ? country : country.filter(v => v.toLowerCase().indexOf(term.toLowerCase()) > -1)).slice(0, 10)); searchNationality = (text$: Observable<string>) => text$ .debounceTime(200).distinctUntilChanged() .merge(this.nationalityFocus$) .merge(this.nationalityClick$.filter(() => !this.nationalityInstance.isPopupOpen())) .map(term => (term === '' ? nationality : nationality.filter(v => v.toLowerCase().indexOf(term.toLowerCase()) > -1)).slice(0, 10)); }
HTML 文件
<div class="col-sm-6"> <div class="form-group"> <input placeholder="Country" id="countrySearch" type="text" name="country" class="form-control" [(ngModel)]="model" [ngbTypeahead]="searchCountry" (focus)="countryFocus$.next($event.target.value)" (click)="countryClick$.next($event.target.value)" #countryInstance="ngbTypeahead" /> </div> </div> <!-- col end --> <div class="col-sm-6"> <div class="form-group"> <input placeholder="Nationality" id="nationalitySearch" type="text" name="Nationality" class="form-control" [(ngModel)]="model1" [ngbTypeahead]="searchNationality" (focus)="nationalityFocus$.next($event.target.value)" (click)="nationalityClick$.next($event.target.value)" #nationalityInstance="ngbTypeahead" /> </div> </div> <!-- col end -->
修改说明
- 给每个Typeahead组件分配了独立的模板引用变量(
#countryInstance和#nationalityInstance),确保@ViewChild能正确获取对应组件的实例 - 为每个组件创建了专属的
focus$和click$事件流,避免跨组件触发事件 - 搜索函数里的弹窗状态判断也对应到了各自的组件实例
这样修改后,两个Typeahead组件就完全独立了,不会再出现互相干扰的弹窗闪烁问题。
内容的提问来源于stack exchange,提问作者Santosh
相关产品推荐
相关产品推荐

