ng-select无法从服务获取数据,仅添加延迟后可正常工作
ng-select无法订阅Observable,仅加delay后显示数据的解决方案
问题描述
使用ng-select实现活动搜索选择功能:用户输入内容时触发服务调用,服务返回匹配的活动数组Observable,ng-select通过async管道订阅该Observable。但ng-select无法正常获取数据,只有给服务返回的Observable添加pipe(delay(100))时才能显示数据,移除延迟后控制台能打印正确数组,但ng-select就是不渲染。
相关代码
HTML代码
<label for="class">Activité</label> <ng-select [items]="activities$ | async" groupBy="type" [(ngModel)]="selectedActivity" [ngModelOptions]="{standalone: true}" (search)="inputChanged($event)"> </ng-select>
组件TypeScript代码
activities$!: Observable<Activity[]>; selectedActivity!: Activity; private modelChanged: Subject<string> = new Subject<string>(); private searchSubscription!: Subscription; debounceTime = 500; constructor(private dataService: DataService) { } ngOnInit(): void { this.initActivitySearchListener(); }; initActivitySearchListener(): void { this.searchSubscription = this.modelChanged .pipe( debounceTime(this.debounceTime), ) .subscribe(input => { console.log(input) this.updateSearch(input); }); } //输入活动搜索框时触发 inputChanged($event: any) { this.modelChanged.next($event.term); } updateSearch(term: string) { console.log(term); this.activities$ = this.dataService.getActivitiesForList(term); this.dataService.getActivitiesForList(term).subscribe(data => console.log(data)); }
服务TypeScript代码
getActivitiesForList(inputString: string): Observable<Activity[]> { let parsedResult: Activity[] = []; this.http.get<any>(this.HOST + this.API_URI + 'activity?searchText=' + inputString).subscribe((data) => { for (const activity of data) { switch (activity.type) { case "dungeon": let tempBossArray: any[] = []; activity.dungeon_boss.forEach((bossObject: any) => { tempBossArray.push(bossObject.name); }); parsedResult.push({ id: activity.id, name: activity.name + ' (' + tempBossArray.toString() + ') ' + '[Niv. ' + activity.level + ']', type: "Donjon" }); break; case "quest": parsedResult.push({ id: activity.id, name: activity.name + ' (' + activity.category + ')', type: "Quête" }); break; default: break; } } }); return of(parsedResult).pipe(delay(100)); };
问题根源
服务的getActivitiesForList方法逻辑错误:
- 直接订阅了
http.get请求,然后同步返回of(parsedResult),此时HTTP请求还没完成,parsedResult是空数组。 - 添加
delay(100)只是巧合让HTTP请求在延迟结束前完成,填充了数组,属于不可靠的临时解决方案。
修复方案
1. 重构服务方法,用RxJS操作符处理数据流
不要手动订阅HTTP请求,改用map操作符在响应返回后处理数据,返回的Observable会正确发出处理后的结果:
getActivitiesForList(inputString: string): Observable<Activity[]> { // 用模板字符串拼接URL更简洁 return this.http.get<any>(`${this.HOST}${this.API_URI}activity?searchText=${inputString}`).pipe( map(data => { const parsedResult: Activity[] = []; for (const activity of data) { switch (activity.type) { case "dungeon": // 用map替代forEach简化数组处理 const tempBossArray = activity.dungeon_boss.map((bossObject: any) => bossObject.name); parsedResult.push({ id: activity.id, name: `${activity.name} (${tempBossArray.toString()}) [Niv. ${activity.level}]`, type: "Donjon" }); break; case "quest": parsedResult.push({ id: activity.id, name: `${activity.name} (${activity.category})`, type: "Quête" }); break; default: break; } } return parsedResult; }) ); }
2. 优化组件订阅逻辑(可选但更规范)
组件中无需手动订阅服务返回的Observable,async管道会自动处理订阅和取消订阅,避免内存泄漏:
updateSearch(term: string) { console.log(term); this.activities$ = this.dataService.getActivitiesForList(term); // 删除手动订阅的代码 // this.dataService.getActivitiesForList(term).subscribe(data => console.log(data)); }
修复原理
- 服务返回的Observable会在HTTP请求完成、数据处理完毕后才发出值,
async管道能正确接收到并更新ng-select的items。 - 去掉
delay后,数据流依然是异步的,ng-select会在数据准备好时自动渲染,无需依赖延迟的巧合。
内容的提问来源于stack exchange,提问作者LittleOne
相关产品推荐
相关产品推荐

