Angular 17登录后Navbar按钮未切换问题求助
登录状态持久化问题(Angular 17 + PrimeNG 17)
提交正确的用户凭证后,服务器验证通过返回JWT Token、refresh token及isLoggedIn=true。当前代码已将token存入localStorage,但导航栏的Login按钮未切换为Logout,登录状态无法持久显示。
现有代码
Navbar.component.ts
export class NavbarComponent { menuItems: MenuItem[] = []; isLoggedIn: Observable<boolean>; ref: DynamicDialogRef | undefined; constructor( public dialogService: DialogService, private authService: AuthserviceService ) { this.isLoggedIn = authService.isLoggedIn(); } ngOnInit() { this.menuItems = [//Menu Items would go here] } OpenLoginDialog(){ this.ref = this.dialogService.open(LoginComponent, { header: 'Login', width: '50vw', modal:true, breakpoints: { '960px': '75vw', '640px': '90vw' }, }) } }
Navbar.component.html
<div> <p-menubar [model]="menuItems"> <div> <input type="text" pInputText placeholder="Search"> <button *ngIf="!isLoggedIn; else loggedOut" type="button" pButton label="Logout" icon="pi pi-power-off" style="margin-left:.25em"></button> <ng-template #loggedOut> <button type="button" pButton (click)="OpenLoginDialog()" label="Login" icon="pi pi-power-off" style="margin-left:.25em"></button> </ng-template> </div> </p-menubar> </div>
Auth.service.ts
export class AuthserviceService { isLoginSubject = new BehaviorSubject<boolean>(this.hasToken()); constructor(private http: HttpClient) { } loginUser<LoginResponse>(loginCreds: any){ return this.http.post(baseUrl + "login", loginCreds) } logoutUser(){ // This is where I delete the local storage } setStorage(data: any){ localStorage.setItem("token", data.jwtToken) localStorage.setItem("refresh", data.refreshToken) // Seem redundant so commented bottom line // localStorage.setItem("isLoggedIn", data.isLoggedIn.toString()) } isLoggedIn(): Observable<boolean>{ return this.isLoginSubject.asObservable() } private hasToken(): boolean{ return !!localStorage.getItem("token") } }
Login.component.ts
export class LoginComponent { loginCredentials: FormGroup; constructor(public ref: DynamicDialogRef, private formBuilder: FormBuilder, private auth: AuthserviceService) { this.loginCredentials = this.formBuilder.group({ alias: ['', Validators.required], password: ['', Validators.required] }) } SubmitCredentials() { console.log("Submitting Creds") this.auth.loginUser<LoginResponse>(this.loginCredentials.value) .subscribe({ next: (data: any) => { console.log('Data: ' + data.jwtToken) console.log('Data: ' + data.refreshToken) console.log('Data: ' + data.isLoggedIn) this.auth.setStorage(data) }, error: (error: HttpErrorResponse) => { console.log('Error: ' + error.error) }}); this.closeDialog(localStorage.getItem("isLoggedIn")) } closeDialog(data: any = null){ this.ref.close(data) } }
问题原因
- BehaviorSubject未同步状态:登录成功后仅存入token到localStorage,但未通知
isLoginSubject更新状态,导致Navbar的订阅者无法感知登录状态变化。 - 模板未正确订阅Observable:
isLoggedIn是Observable类型,模板直接使用!isLoggedIn无法获取实时值,必须通过async管道订阅。 - 对话框关闭时机错误:
closeDialog在subscribe外部调用,可能登录请求尚未完成就关闭对话框,且未依赖AuthService的状态更新。 - ngIf逻辑与模板命名冲突:
*ngIf="!isLoggedIn; else loggedOut"逻辑反转,loggedOut模板实际显示Login按钮,命名和逻辑不匹配易导致混淆。
解决方案
1. 更新AuthService,同步状态变化
修改setStorage方法,存入token后更新BehaviorSubject;完善logoutUser方法,清除存储并同步状态:
export class AuthserviceService { isLoginSubject = new BehaviorSubject<boolean>(this.hasToken()); constructor(private http: HttpClient) { // 可选:监听localStorage变化,处理多标签页登录状态同步 window.addEventListener('storage', (event) => { if (event.key === 'token') { this.isLoginSubject.next(this.hasToken()); } }); } loginUser<LoginResponse>(loginCreds: any){ return this.http.post(baseUrl + "login", loginCreds) } logoutUser(){ localStorage.removeItem("token"); localStorage.removeItem("refresh"); this.isLoginSubject.next(false); } setStorage(data: any){ localStorage.setItem("token", data.jwtToken) localStorage.setItem("refresh", data.refreshToken) // 更新登录状态通知所有订阅者 this.isLoginSubject.next(true); } isLoggedIn(): Observable<boolean>{ return this.isLoginSubject.asObservable() } private hasToken(): boolean{ return !!localStorage.getItem("token") } }
2. 修正Navbar模板,正确使用async管道
调整ngIf逻辑和模板命名,确保通过async管道订阅Observable:
<div> <p-menubar [model]="menuItems"> <div> <input type="text" pInputText placeholder="Search"> <!-- 登录状态显示Logout按钮,否则显示Login按钮 --> <button *ngIf="isLoggedIn | async; else loginTemplate" type="button" pButton label="Logout" icon="pi pi-power-off" style="margin-left:.25em" (click)="authService.logoutUser()"></button> <ng-template #loginTemplate> <button type="button" pButton (click)="OpenLoginDialog()" label="Login" icon="pi pi-power-off" style="margin-left:.25em"></button> </ng-template> </div> </p-menubar> </div>
3. 调整LoginComponent对话框关闭时机
将closeDialog移到subscribe的next回调中,确保登录成功后再关闭对话框:
export class LoginComponent { loginCredentials: FormGroup; constructor(public ref: DynamicDialogRef, private formBuilder: FormBuilder, private auth: AuthserviceService) { this.loginCredentials = this.formBuilder.group({ alias: ['', Validators.required], password: ['', Validators.required] }) } SubmitCredentials() { console.log("Submitting Creds") this.auth.loginUser<LoginResponse>(this.loginCredentials.value) .subscribe({ next: (data: any) => { console.log('Data: ' + data.jwtToken) console.log('Data: ' + data.refreshToken) console.log('Data: ' + data.isLoggedIn) this.auth.setStorage(data); // 登录成功后关闭对话框 this.closeDialog(); }, error: (error: HttpErrorResponse) => { console.log('Error: ' + error.error) } }); } closeDialog(data: any = null){ this.ref.close(data) } }
额外说明
- 页面刷新时,
isLoginSubject初始值由hasToken()从localStorage读取,确保刷新后状态保持一致。 - 多标签页状态同步通过监听
storage事件实现,可选但能提升用户体验。
内容的提问来源于stack exchange,提问作者DonDavid12
相关产品推荐
相关产品推荐

