Angular Firestore异步函数触发两次问题排查与解决
解决Angular中点击按钮触发两次函数调用的问题
我来帮你分析这个问题的原因和对应的修复方案:
一、最可能的触发原因:事件冒泡
你的删除按钮结构是 <a> 标签内部嵌套了 <i> 图标和文字,当你点击图标或者文字时,点击事件会从内部的 <i> 元素冒泡到父级的 <a> 标签,导致绑定在 <a> 上的 (click) 事件被触发两次——这是这类问题最常见的诱因。
二、快速修复:阻止事件冒泡
直接在点击事件中阻止事件向上冒泡,就能解决重复触发的问题,有两种简单写法:
写法1:调用stopPropagation()
<a style="color:white;" id="{{product.id}}" (click)="removeFromCart($event); $event.stopPropagation();" class="btn btn-danger "> <i class="fa fa-trash"></i> Remove </a>
写法2:返回false(同时阻止冒泡和默认行为)
<a style="color:white;" id="{{product.id}}" (click)="removeFromCart($event); return false;" class="btn btn-danger "> <i class="fa fa-trash"></i> Remove </a>
三、优化代码:避免嵌套订阅的潜在问题
你的函数里嵌套了多层 subscribe,每次调用都会创建新的订阅,如果不及时清理,不仅可能加剧重复执行的问题,还会造成内存泄漏。建议用RxJS的操作符重构代码,扁平化订阅链:
removeFromCart(event) { // 先阻止事件冒泡,从根源避免重复触发 event.stopPropagation(); const target = event.target || event.srcElement || event.currentTarget; const productId = target.attributes.id.value; this.authService.getAuth() .pipe( // 只处理已登录的情况 filter(auth => !!auth), switchMap(auth => { this.isLoggedIn = true; const loggedInUserEmail = auth.email; return this.userService.getUsers().pipe( // 用find代替filter,直接获取匹配的用户(更高效) map(users => users.find(user => user.email === loggedInUserEmail)), // 确保找到用户再继续 filter(user => !!user) ); }), switchMap(currentUser => { this.currentUser = currentUser; return this.cartService.getCarts(currentUser).pipe( map(carts => carts[0]), filter(cart => !!cart) ); }), switchMap(mycart => { this.mycart = mycart; return this.cartService.getCartProduct(productId, this.currentUser, mycart); }), // 只执行一次,自动完成订阅,避免内存泄漏 take(1) ) .subscribe({ next: (productToDelete) => { this.productToDelete = productToDelete; this.productService.deleteProductFromCart(productToDelete, this.currentUser, this.mycart); }, error: (err) => { console.error('移除商品出错:', err); this.isLoggedIn = false; } }); }
重构后的优势:
- 用
switchMap扁平化嵌套订阅,代码结构更清晰 - 用
filter和find确保数据有效性,避免空指针异常 - 用
take(1)自动取消订阅,彻底解决内存泄漏和重复执行风险 - 统一错误处理,代码健壮性更强
内容的提问来源于stack exchange,提问作者Sabeeh Ul Haq
相关产品推荐
相关产品推荐

