You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中Navbar显示控制及用户信息实时更新问题

解决Angular Navbar登录状态同步与页面显示控制问题

问题核心原因

  1. 认证服务未使用可观察对象实时推送用户状态变化,导致组件无法自动感知登录/注销操作
  2. MainNavComponent错误订阅了登录请求方法(login()),而非用户状态流
  3. ngOnChanges未正确触发(仅监听组件输入属性),无法同步本地存储变化
  4. 未结合路由状态控制Navbar在登录页的显示逻辑

步骤1:改造认证服务,实现状态实时推送

使用BehaviorSubject维护用户状态,让所有组件能订阅并实时获取状态更新:

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, BehaviorSubject, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { environment } from '../environments/environment';
import { User, UserModel } from './user.model';

@Injectable({ providedIn: 'root' })
export class AuthenticationService {
  // 用BehaviorSubject存储用户状态,初始值从localStorage读取
  private currentUserSubject = new BehaviorSubject<User | null>(this.getUserFromLocalStorage());
  // 对外暴露不可修改的Observable
  public currentUser$ = this.currentUserSubject.asObservable();

  constructor(private http: HttpClient, private router: Router) {}

  login(): Observable<User> {
    const params = new HttpParams().append('param', 'value');
    return this.http.get<User>(`${environment.apiUrl}azure` + location.search, { params }).pipe(
      tap((response: any) => {
        if (response && response.token) {
          // 存储到localStorage
          localStorage.setItem('TOKEN_KEY', response.token);
          localStorage.setItem('username', response.user_name);
          localStorage.setItem('useremail', response.user_email);
          // 更新BehaviorSubject状态
          const user: User = {
            token: response.token,
            user_email: response.user_email,
            user_name: response.user_name,
            validity: response.validity || 0
          };
          this.currentUserSubject.next(user);
        } else {
          this.router.navigate(['/login']);
        }
      }),
      catchError(error => {
        console.error('Login Error:', error);
        return throwError(error);
      })
    );
  }

  isLoggedIn(): boolean {
    return !!localStorage.getItem('TOKEN_KEY');
  }

  // 从localStorage读取用户信息
  private getUserFromLocalStorage(): User | null {
    const username = localStorage.getItem('username');
    const useremail = localStorage.getItem('useremail');
    const token = localStorage.getItem('TOKEN_KEY');
    
    if (token && username && useremail) {
      return {
        token,
        user_email: useremail,
        user_name: username,
        validity: 0
      } as User;
    }
    return null;
  }

  logout() {
    localStorage.removeItem('TOKEN_KEY');
    localStorage.removeItem('username');
    localStorage.removeItem('useremail');
    // 更新状态为null
    this.currentUserSubject.next(null);
    this.router.navigate(['/login']);
  }
}

步骤2:修改MainNavComponent,订阅状态流并同步更新

移除无效的ngOnChanges和错误的订阅,改为订阅认证服务的currentUser$:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';
import { User } from '../models/user.model';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-main-nav',
  templateUrl: './main-nav.component.html',
  styleUrls: ['./main-nav.component.scss']
})
export class MainNavComponent implements OnInit, OnDestroy {
  currentUser: User | null = null;
  private subscription: Subscription = new Subscription();

  constructor(
    private authService: AuthenticationService,
    private router: Router,
    private activatedRoute: ActivatedRoute
  ) {}

  ngOnInit() {
    // 订阅用户状态变化
    this.subscription.add(
      this.authService.currentUser$.subscribe(user => {
        this.currentUser = user;
      })
    );
  }

  logout() {
    this.authService.logout();
  }

  // 判断当前是否为登录页
  isLoginPage(): boolean {
    return this.router.url === '/login';
  }

  // 销毁时取消订阅,避免内存泄漏
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

步骤3:控制Navbar在登录页的显示逻辑

修改模板的*ngIf条件,结合用户状态和路由状态控制Navbar显示:

<mat-sidenav-content>
  <!-- 仅当用户已登录且当前不是登录页时显示Navbar -->
  <mat-toolbar *ngIf="currentUser && !isLoginPage()" class="crm-main-toolbar">
    <button mat-icon-button (click)="sidenav.toggle()" class="menu-togle">
      <mat-icon class="material-symbols-outlined" *ngIf="sidenav.opened">chevron_left</mat-icon>
      <mat-icon class="material-symbols-outlined" *ngIf="!sidenav.opened">menu</mat-icon>
    </button>
    <img src="/assets/icons/logo-white.svg" alt="" class="logo" fxShow.xs="false" routerLink="/dashboard"/>
    <img src="/assets/icons/logotype.svg" alt="" class="logo-mobile" fxHide.xs="false" fxHide.gt-xs="true" routerLink="/dashboard"/>
    <span class="toolbar-spacer"></span>
    <p class="user-name" fxShow.xs="false">{{ currentUser.user_name}}</p>
    <button mat-icon-button [matMenuTriggerFor]="userMenu" class="circle-button white user-menu">
      <i class="zwicon-user"></i>
    </button>
    <mat-menu #userMenu="matMenu">
      <button mat-menu-item routerLink="/login">
        <mat-icon class="material-symbols-outlined">account_circle</mat-icon>
        My profile
      </button>
      <button mat-menu-item (click)="logout()">
        <mat-icon class="material-symbols-outlined">logout</mat-icon>
        Logout
      </button>
    </mat-menu>
  </mat-toolbar>
  <div id="content" [ngClass]="{'content-padding' : currentUser}">
    <router-outlet></router-outlet>
  </div>
</mat-sidenav-content>

关键验证点

  1. 登录后无需刷新页面,Navbar自动显示且用户名实时更新
  2. 注销后Navbar立即隐藏,路由跳转到登录页
  3. 直接访问登录页时,Navbar不会显示
  4. 刷新页面时,会从localStorage读取状态并恢复Navbar显示

内容的提问来源于stack exchange,提问作者weinde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:02:09