Angular 5中Material 2 Dialog内Subject鉴权状态不更新问题
你遇到的核心问题是普通Subject无法在新创建的Dialog组件中同步最新鉴权状态,再加上初始状态缺失,导致模板里的*ngIf始终拿不到正确的布尔值,才会不管用户是否登录都显示登录/注册按钮。下面一步步帮你搞定:
1. 把Subject替换为BehaviorSubject
你的userSignedIn$用的是普通Subject,它不会保存当前的状态值——当MobileMenuComponent(Dialog里的组件)初始化并订阅时,如果validateToken已经执行过并发出过值,这个新订阅会完全错过之前的发射,导致模板里的async管道拿到的是null或者未定义状态,从而让!(authService.userSignedIn$ | async)始终为true。
修改AuthService里的状态定义:
// 替换原来的Subject,给一个初始默认值false userSignedIn$: BehaviorSubject<boolean> = new BehaviorSubject(false); constructor(public authService: Angular2TokenService) { this.authService.validateToken().subscribe( res => { const isSignedIn = res.status === 200 && res.json().success; this.userSignedIn$.next(isSignedIn); }, // 别忘了处理错误情况,比如token验证失败也要设为false () => this.userSignedIn$.next(false) ) }
2. 确保登录/登出时同步更新状态
现在你的logOutUser方法里没有更新userSignedIn$的值,登出后状态不会同步;同样登录成功后也要记得调用userSignedIn$.next(true):
在AuthService里补充状态更新逻辑:
loginUser(credentials) { return this.authService.signIn(credentials).subscribe( () => this.userSignedIn$.next(true), () => this.userSignedIn$.next(false) ); } logOutUser() { return this.authService.signOut().pipe( tap(() => this.userSignedIn$.next(false)) // 登出成功后立即更新状态 ); }
3. 简化模板订阅(可选但推荐)
你在模板里多次重复(authService.userSignedIn$ | async),虽然Angular会自动共享订阅,但抽成一个变量能提升可读性和性能:
<div class="mobile-menu fluid-container"> <!-- ... 其他内容 ... --> <div class="col-12 menu-items"> <ul class="navbar-nav mr-auto" *ngIf="(authService.userSignedIn$ | async) as isSignedIn"> <!-- ... 固定菜单项 ... --> <li *ngIf="isSignedIn" class="nav-item"> <a class="nav-link" id="profile" (click)="forward('/profile')">Profile</a> </li> <li *ngIf="isSignedIn" class="nav-item"> <a (click)="logout()" class="nav-link">Logout</a> </li> <li *ngIf="!isSignedIn" class="nav-item"> <a class="nav-link" (click)="presentAuthDialog('login')" >Login</a> </li> <li *ngIf="!isSignedIn" class="nav-item"> <a class="nav-link" (click)="presentAuthDialog('register')">Register</a> </li> </ul> </div> </div>
4. 清理冗余调试代码
你之前在MobileMenuComponent里加的userLogedin变量和checkAuthStatus订阅完全没必要——async管道会帮你自动管理订阅,而且手动订阅如果不取消还可能导致内存泄漏。直接删掉这些调试代码,用模板里的async管道就够了。
为什么桌面版正常?
因为桌面导航组件在应用初始化时就已经存在,它的订阅能捕获到Subject发出的初始validateToken结果;但MobileMenuComponent是在Dialog打开时才创建的,这时候Subject已经发过值了,新订阅拿不到历史值,所以状态始终不对。换成BehaviorSubject后,新订阅会立即收到最新的状态值,问题自然就解决了。
内容的提问来源于stack exchange,提问作者Paul Etscheit

