RxJS实现多按钮点击API调用:如何重新启用前一个点击的按钮?
按钮点击联动API调用的状态管理问题
场景描述
UI中有一组可点击按钮,点击按钮时需禁用该按钮并触发API调用;若点击另一个按钮,需要取消当前正在进行的API调用,初始化新的API调用,同时重新启用前一个被点击的按钮。
现有代码
@ViewChildren('shape') shapes!: QueryList<ElementRef>; const shapes$: Array<Observable<PointerEvent>> = this.shapes.map((ele) => fromEvent(ele.nativeElement, 'click')); merge(...shapes$) .pipe( map((e: PointerEvent) => (e.target as HTMLButtonElement)), tap((e) => e.disabled = true), map((e: HTMLButtonElement) => e.getAttribute('shape') as ShapeNames), switchMap((shape) => this.makeAPICall(shape)), // How do i enable the previously clicked button. ) .subscribe((...res) => { console.log(res); });
待解决问题
- 点击第二个按钮时,如何重新启用前一个被点击的按钮?
- 如何在
switchMap之后访问前一个点击的元素?
解决方案
1. 跟踪前一个按钮,实现切换时重新启用
使用scan操作符维护流的状态,记录上一次点击的按钮引用。每次新按钮被点击时,先启用前一个按钮,再禁用当前按钮:
@ViewChildren('shape') shapes!: QueryList<ElementRef>; const shapes$: Array<Observable<PointerEvent>> = this.shapes.map((ele) => fromEvent(ele.nativeElement, 'click')); merge(...shapes$) .pipe( map((e: PointerEvent) => (e.target as HTMLButtonElement)), // 用scan跟踪上一个按钮,切换时重置状态 scan((prevBtn: HTMLButtonElement | null, currBtn: HTMLButtonElement) => { if (prevBtn) prevBtn.disabled = false; currBtn.disabled = true; return currBtn; }, null), map((btn: HTMLButtonElement) => btn.getAttribute('shape') as ShapeNames), switchMap((shape) => this.makeAPICall(shape)) ) .subscribe((res) => { console.log(res); });
2. 在switchMap之后访问点击元素
将按钮元素与shape值打包成对象传递,这样在API调用完成后仍能关联到对应的按钮:
@ViewChildren('shape') shapes!: QueryList<ElementRef>; const shapes$: Array<Observable<PointerEvent>> = this.shapes.map((ele) => fromEvent(ele.nativeElement, 'click')); merge(...shapes$) .pipe( map((e: PointerEvent) => (e.target as HTMLButtonElement)), scan((prevBtn: HTMLButtonElement | null, currBtn: HTMLButtonElement) => { if (prevBtn) prevBtn.disabled = false; currBtn.disabled = true; return currBtn; }, null), // 打包按钮和shape值,保留关联关系 map((btn) => ({ btn, shape: btn.getAttribute('shape') as ShapeNames })), switchMap(({ btn, shape }) => // API调用完成后,返回结果与按钮的关联对象 this.makeAPICall(shape).pipe(map(result => ({ result, btn }))) ), // 现在可以直接访问按钮元素 tap(({ result, btn }) => { console.log('API结果:', result); console.log('对应按钮:', btn); // 可选:API完成后启用当前按钮 btn.disabled = false; }) ) .subscribe();
补充说明
switchMap会自动取消前一个未完成的API调用,无需额外处理取消逻辑。- 若API调用可能抛出错误,建议在
switchMap中添加catchError,避免整个流中断,同时处理错误场景下的按钮状态。
内容的提问来源于stack exchange,提问作者harikrish
相关产品推荐
相关产品推荐

