Ionic Angular模态框关闭触发栈溢出错误求助
问题描述
我用Ionic模态框实现了一个注册新用户的页面组件,需要执行两个异步操作:一是在Firebase中注册用户,二是往数据库写入用户信息。但执行后模态框关闭,页面退回上一页,控制台抛出Uncaught RangeError: Maximum call stack size exceeded错误。单独调用其中一个Promise时功能完全正常,另外有个结构类似但仅调用单个Promise的页面也没有问题。
相关代码
register.page.html
<ion-content> <ion-fab (click)="setOpen('isModalOpen', true)" vertical="bottom" horizontal="end" slot="fixed"> <ion-fab-button id="user-modal"> <ion-icon name="add"></ion-icon> </ion-fab-button> </ion-fab> <ion-modal trigger="user-modal"> <ng-template> <ion-header> <ion-toolbar> <ion-buttons slot="start"> <ion-button (click)="cancel()"> <ion-icon name="arrow-back-outline"></ion-icon> </ion-button> </ion-buttons> <ion-title>Create User</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-item class="new-user"> <ion-label position="floating">Email:</ion-label> <ion-input type="email" [(ngModel)]="email"></ion-input> </ion-item> <ion-button (click)="sendUser()">Cadastrar</ion-button> </ion-content> </ng-template> </ion-modal> </ion-content>
register.page.ts
@Component({ selector: 'register-admin', templateUrl: './register-admin.page.html', styleUrls: ['./register-admin.page.scss'], }) export class RegisterPage implements OnInit { @ViewChild(IonModal) modal: IonModal; constructor(private userService: UserService) {} sendUser() { this.userService.createUser(user) .then(() => { this.userService.createUserInfo(user) .then(() => { alert('User created!'); this.modal.dismiss(null, 'cancel'); }); }).catch(err => { if (err.message.includes('already')) { alert('Already Created'); } else if (err.message.includes('Password')) { alert('Password have to be more than 6 digits.'); } else { alert('Try again Later'); } }); }
user.service.ts
createUser(user) { return this.fireAuth.createUserWithEmailAndPassword(user.email, user.pass) } resetPassword(email: string) { return this.fireAuth.sendPasswordResetEmail(email); } createUserInfo(user: any) { return this.db.list('/user').push({ name: user.name, email: user.email, admin: user.admin === 'true' ? true : false, type: user.type }); }
控制台错误信息
Uncaught RangeError: Maximum call stack size exceeded. at focusElement (helpers-eed79a2b.js:158:1) at focusFirstDescendant (overlays-4fbe89bd.js:4095:17) at trapShadowFocus (overlays-4fbe89bd.js:4235:7) at trapKeyboardFocus (overlays-4fbe89bd.js:4252:5) at HTMLDocument.<anonymous> (overlays-4fbe89bd.js:4261:41) at ZoneDelegate.invokeTask (zone.js:434:1) at Zone.runTask (zone.js:205:1) at ZoneTask.invokeTask [as invoke] (zone.js:516:1) at invokeTask (zone.js:1656:1) at HTMLDocument.globalZoneAwareCaptureCallback (zone.js:1725:1)
注:错误也可能来自focusLastDescendant。我排查过模态框焦点问题,但单Promise调用时一切正常,不确定是否受项目其他部分影响。
解决方案
这个问题大概率是模态框关闭时的焦点循环触发了递归调用,结合双Promise连续执行的场景,异步操作完成后立即关闭模态框时,Ionic的焦点陷阱逻辑出现了无限递归。可以尝试以下修复方案:
1. 给模态框关闭操作加短暂延迟
让异步操作完全结束后再处理模态框关闭,避免时序冲突:
sendUser() { this.userService.createUser(user) .then(() => { this.userService.createUserInfo(user) .then(() => { alert('User created!'); // 加100ms延迟,等待异步上下文清理完成 setTimeout(() => { this.modal.dismiss(null, 'cancel'); }, 100); }); }).catch(err => { if (err.message.includes('already')) { alert('Already Created'); } else if (err.message.includes('Password')) { alert('Password have to be more than 6 digits.'); } else { alert('Try again Later'); } }); }
2. 改用async/await优化Promise嵌套
嵌套Promise可能导致上下文处理异常,用async/await让代码线性化,避免异步嵌套带来的时序问题:
async sendUser() { try { await this.userService.createUser(user); await this.userService.createUserInfo(user); alert('User created!'); setTimeout(() => { this.modal.dismiss(null, 'cancel'); }, 100); } catch (err) { if (err.message.includes('already')) { alert('Already Created'); } else if (err.message.includes('Password')) { alert('Password have to be more than 6 digits.'); } else { alert('Try again Later'); } } }
3. 移除重复的模态框触发逻辑
你同时使用了(click)事件和trigger属性绑定模态框打开逻辑,可能导致重复的状态变更冲突。可以移除其中一种方式,比如保留trigger绑定:
<ion-fab vertical="bottom" horizontal="end" slot="fixed"> <ion-fab-button id="user-modal"> <ion-icon name="add"></ion-icon> </ion-fab-button> </ion-fab> <ion-modal trigger="user-modal"> <!-- 其余内容不变 --> </ion-modal>
4. 手动指定关闭后的焦点元素
在关闭模态框前,手动将焦点移到外部元素,打断焦点陷阱的递归循环:
sendUser() { this.userService.createUser(user) .then(() => { this.userService.createUserInfo(user) .then(() => { alert('User created!'); // 关闭前将焦点移到fab按钮 document.getElementById('user-modal').focus(); this.modal.dismiss(null, 'cancel'); }); }).catch(err => { // 错误处理不变 }); }
内容的提问来源于stack exchange,提问作者Giovannigv
相关产品推荐
相关产品推荐

