我对 Angular 相当陌生,目前正在使用它构建一个 Web 应用程序。我的问题在于更新一个组件中的值,然后在另一个组件中更改该值,而这两个组件彼此无关。为了实现这一目标,我尝试使用不起作用的订阅。
我的根组件的 html (app.component.html) 看起来像这样:
<app-navbar></app-navbar>
<router-outlet></router-outlet>
在路由器出口内,除非服务器设置并验证了用户 ID 和令牌,否则用户会自动重定向到登录页面(每个页面都是一个组件)。设置用户 ID 和令牌并将其存储为 Cookie 后,我希望在导航栏中显示一个注销按钮。
navbar.component.html:
<nav class="bg-dark border-bottom border-body" data-bs-theme="dark">
<a routerLink="">Home</a>
<button *ngIf="isAuthenticated" routerLink="login" class="logout">Log out</button>
</nav>
navbar.component.ts:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit, OnDestroy {
isAuthenticated: boolean = false
authSubscription: Subscription
constructor(private authService: AuthService) {
}
ngOnInit(): void {
this.authSubscription = this.authService.isAuthenticated$.subscribe((isAuthenticated) =>
{
this.isAuthenticated = isAuthenticated
}
)
}
ngOnDestroy(): void {
this.authSubscription.unsubscribe()
}
}
我正在使用 auth.service.ts 来验证用户身份:
import { Injectable } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { AUTHENTICATE } from './graphql/graphql.queries';
import { Apollo } from 'apollo-angular';
import { BehaviorSubject, Observable, firstValueFrom } from 'rxjs';
@Injectable({
providedIn: "root",
})
export class AuthService {
isAuthenticated = false;
error: any
private isAuthenticatedBS = new BehaviorSubject<boolean>(this.isAuthenticated)
public isAuthenticated$ : Observable<boolean> = this.isAuthenticatedBS.asObservable()
constructor(private cookieService: CookieService, private apollo: Apollo) {
this.checkAuthentication()
}
async checkAuthentication(): Promise<boolean> {
this.isAuthenticated = await this.authenticate()
return this.isAuthenticated;
}
async authenticate()
{
if(this.cookieService.get("userId") && this.cookieService.get("token"))
{
const response = await firstValueFrom(this.apollo.query({
query: AUTHENTICATE,
variables: {
userId: +this.cookieService.get("userId"),
},
fetchPolicy: 'network-only',
}));
if(<number>(<any>response.data).authenticate != null)
{
if(<number>(<any>response.data).authenticate.id > 0)
{
return true
}
}
return false
}
else
{
return false
}
}
}
问题是 navbar.component.ts 中的“isAuthenticated”属性似乎从未更新。
我的尝试和期望
应该发生的事情是,一旦我使用正确的电子邮件和密码登录,我就会收到一个令牌并将令牌和用户 ID 存储为 cookie(这部分肯定有效),然后重定向到主页。每次用户尝试导航到需要身份验证的页面时,auth.guard 都会调用 auth.service 的 checkAuthentication 方法来检查具有给定 id 和令牌的用户是否存在。登录后,注销按钮应出现在导航栏中并一直保留,直到用户决定注销并删除存储用户 ID 和令牌的 Cookie。
我尝试手动触发更改检测并使用控制台日志检查身份验证是否成功。根据 chatgpt 的建议,如果您想了解有关组件状态更改的更多详细信息,我已经安装了 chrome 的 Angular devtools 扩展。
在您的 AuthService 中,布尔值
isAuthenticated
未连接到 isAuthenticatedBS
。您可以使用 isAuthenticated
所具有的值(即 false
)初始化 BehaviourSubject,但对 isAuthenticated
的更改不会触发主题。主题有其自己的内部值,您无需将其“连接”到现有变量就这样。
相反,如果您希望 BehaviourSubject 发出新值,则需要对其调用
next(value)
。因此,我建议从 AuthService 中删除 isAuthenticated
布尔值,并在 isAuthenticatedBS.next(await this.authenticate())
方法中调用 checkAuthentication
。
如果您需要当前值而不是可观察值,您可以只查询
isAuthenticatedBS.value