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

Angular认证Signal登录后状态不更新,登出正常问题求助

问题:登录后状态不更新,需刷新页面才生效

问题场景与代码

我有一个展示登录状态的组件,相关代码如下:

组件模板代码

{{loggedIn()}}

组件TS代码

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, WritableSignal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { RouterOutlet } from '@angular/router';
import { CompanyLoginComponent } from './company-login/company-login.component';
import { AuthService } from './auth.service';
import { AsyncPipe } from '@angular/common';
import { MatSidenavModule } from '@angular/material/sidenav';

@Component({
    selector: 'app-company-facing',
    standalone: true,
    imports: [
        MatToolbarModule,
        MatButtonModule,
        MatIconModule,
        RouterOutlet,
        CompanyLoginComponent,
        AsyncPipe,
        MatSidenavModule,
    ],
    templateUrl: './company-facing.component.html',
    styleUrl: './company-facing.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush,
    providers: [AuthService,]
})
export class CompanyFacingComponent {
    constructor(private auth: AuthService, private cdr: ChangeDetectorRef) {}

    get loggedIn() {
        return this.auth.loggedIn;
    }

    async logout() {
        await this.auth.logout();
    }
    
    forceChangeDetection() {
        this.cdr.detectChanges();
    }
}

AuthService代码

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { signal, WritableSignal } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  loggedIn: WritableSignal<boolean> = signal(false);

  constructor(private http: HttpClient) {
    this.loggedIn.set(!!localStorage.getItem('accessToken'));
  }

  async login(email: string, password: string) {
    let res = await firstValueFrom(
      this.http.post<{ access_token: string }>('https://api.self-serve.just-solve.com/api/login', { email, password })
    );
    console.log(res);
    if (res.access_token) {
      this.loggedIn.set(true);
      console.log("logged in updated to true!");
    }
    console.log(this.loggedIn());
    localStorage.setItem('accessToken', res.access_token);
    return res;
  }

  async logout() {
    try {
      let res = await firstValueFrom(
        this.http.post('https://api.self-serve.just-solve.com/api/logout', {})
      );
      this.loggedIn.set(false);
    } catch (e) {
      console.error(e);
    }
    localStorage.removeItem('accessToken');
  }
}

当前问题

登出时登录状态能正确切换为false,但登录后状态仍保持false,必须刷新页面才能更新。


原因分析

核心问题是服务实例不统一:

  • AuthService本身配置了providedIn: 'root',属于根注入的单例服务;
  • 但CompanyFacingComponent的元数据里又在providers数组中声明了AuthService,这会导致组件自己创建一个局部的AuthService实例,和CompanyLoginComponent(以及其他组件)注入的根实例不是同一个对象;
  • 登录时,CompanyLoginComponent调用的是根实例的login方法,更新的是根实例的loggedIn信号,但CompanyFacingComponent绑定的是自己局部实例的loggedIn信号,所以状态不会同步;
  • 登出时,是CompanyFacingComponent自己调用局部实例的logout方法,更新的是局部实例的信号,所以看起来状态生效了。

解决方案

直接移除CompanyFacingComponent的providers数组中的AuthService,让组件使用根注入的单例AuthService:

修改后的组件元数据部分:

@Component({
    selector: 'app-company-facing',
    standalone: true,
    imports: [
        MatToolbarModule,
        MatButtonModule,
        MatIconModule,
        RouterOutlet,
        CompanyLoginComponent,
        AsyncPipe,
        MatSidenavModule,
    ],
    templateUrl: './company-facing.component.html',
    styleUrl: './company-facing.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush
    // 移除了 providers: [AuthService,] 这一行
})

这样调整后,所有组件共享同一个AuthService实例,登录时更新loggedIn信号后,Angular的OnPush变更检测会自动响应信号的变化,模板中的{{loggedIn()}}会实时更新登录状态,无需手动调用forceChangeDetection方法。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:37:49