Angular中*ngIf结合Firebase认证导致用户菜单DOM无响应求助
解决Angular中Firebase AuthState结合
*ngIf导致用户菜单无响应的问题 我遇到过类似的场景,问题的核心原因是:Angular的*ngIf会在authState异步变化时销毁并重建整个.user-menu元素,而你使用的模板里控制菜单显示/隐藏的原生JavaScript逻辑,只会在页面初始加载时绑定一次事件。当新的.user-menu元素被渲染出来时,这些事件绑定已经失效了,所以点击操作没有任何反应。而换成*ngIf="1==1"时,DOM元素在页面加载时就存在,原生JS能正常绑定事件,菜单自然可以正常展开。
下面给你两种可行的解决方案:
方案一:用Angular内置指令接管菜单显示逻辑
把模板的CSS控制逻辑迁移到Angular组件中,完全用Angular的方式管理菜单状态,避免依赖原生JS的事件绑定,这也是更符合Angular开发习惯的方案:
修改HTML代码
<div class="right-side"> <!-- Header Widget --> <div class="header-widget"> <!-- User Menu --> <div class="user-menu" *ngIf="afAuth.authState | async as user; else showLogin"> <!-- 点击用户名时切换菜单状态 --> <div class="user-name" (click)="isMenuOpen = !isMenuOpen"> <span><img src="assets/images/agent-03.jpg" alt=""></span>Hi, {{ user.displayName || 'User' }}! </div> <!-- 用ngStyle绑定CSS属性,控制菜单显示/隐藏 --> <ul [ngStyle]="{ visibility: isMenuOpen ? 'visible' : 'hidden', opacity: isMenuOpen ? 1 : 0, transform: isMenuOpen ? 'translate3d(0,0,0)' : 'translate3d(0,15px,0)' }" > <li><a routerLink="my/profile"><i class="sl sl-icon-user"></i> My Profile</a></li> <li><a routerLink="my/bookmarks"><i class="sl sl-icon-star"></i> Bookmarks</a></li> <li><a routerLink="my/properties"><i class="sl sl-icon-docs"></i> My Properties</a></li> <li (click)="logOut()"><a routerLink="/"><i class="sl sl-icon-power"></i> Log Out</a></li> </ul> </div> <ng-template #showLogin> <a routerLink="/login-register" class="sign-in"><i class="fa fa-user"></i> Log In / Register</a> </ng-template> <a routerLink="/submit-property" class="button border">Submit Property</a> </div> <!-- Header Widget / End --> </div>
修改TS代码
在组件中添加控制菜单状态的变量:
import { AngularFireAuth } from 'angularfire2/auth'; @Component({ // ... 你的组件配置 }) export class AppComponent { isMenuOpen = false; // 控制菜单展开/收起的状态变量 constructor(public afAuth: AngularFireAuth) { } logOut() { this.afAuth.signOut(); this.isMenuOpen = false; // 登出后自动关闭菜单 } }
方案二:重新绑定原生事件(适合保留模板原有逻辑)
如果你想继续沿用模板自带的CSS和交互逻辑,需要在Angular渲染完.user-menu元素后,手动重新绑定点击事件:
修改HTML代码
给.user-menu添加模板引用变量,方便在TS中获取元素:
<div class="user-menu" #userMenu *ngIf="afAuth.authState | async as user; else showLogin"> <!-- 原有的菜单内容 --> </div>
修改TS代码
在组件中监听authState变化,每次DOM重建后重新初始化菜单事件:
import { AngularFireAuth } from 'angularfire2/auth'; import { ViewChild, ElementRef, AfterViewInit } from '@angular/core'; @Component({ // ... 你的组件配置 }) export class AppComponent implements AfterViewInit { @ViewChild('userMenu') userMenu!: ElementRef<HTMLElement>; constructor(public afAuth: AngularFireAuth) { } ngAfterViewInit(): void { // 监听authState变化,用户登录后重新初始化菜单 this.afAuth.authState.subscribe(user => { if (user) { // 用setTimeout确保DOM已经渲染完成 setTimeout(() => { this.initUserMenu(); }, 0); } }); } initUserMenu(): void { const userNameEl = this.userMenu.nativeElement.querySelector('.user-name'); const menuUl = this.userMenu.nativeElement.querySelector('ul'); if (!userNameEl || !menuUl) return; // 绑定用户名点击事件 userNameEl.addEventListener('click', () => { const isVisible = menuUl.style.visibility === 'visible'; menuUl.style.visibility = isVisible ? 'hidden' : 'visible'; menuUl.style.opacity = isVisible ? 0 : 1; menuUl.style.transform = isVisible ? 'translate3d(0,15px,0)' : 'translate3d(0,0,0)'; }); // 点击页面其他区域关闭菜单 document.addEventListener('click', (e) => { if (!this.userMenu.nativeElement.contains(e.target as Node)) { menuUl.style.visibility = 'hidden'; menuUl.style.opacity = 0; menuUl.style.transform = 'translate3d(0,15px,0)'; } }); } logOut() { this.afAuth.signOut(); } }
两种方案都能解决问题,方案一的耦合度更低,更适合长期维护;方案二则可以最大程度保留模板原有的交互逻辑,适合快速适配现有项目。
内容的提问来源于stack exchange,提问作者mondieki
相关产品推荐
相关产品推荐

