Angular 4组件间布尔值共享问题:*ngIf控制登录登出显示失效
你遇到的核心问题是:两个路由组件(AppComponent和LoginComponent)各自维护了独立的isValid变量,导致Login里修改的值无法同步到AppComponent的视图中。因为这两个组件是通过路由切换的,不是传统的父模板嵌套子组件,所以直接的组件间通讯(比如@Input/@Output)不适用,最佳方案是用Angular的共享服务来统一管理登录状态。
下面是具体的实现步骤:
1. 创建共享身份验证服务
这个服务会保存登录状态,并通过可观察对象让其他组件订阅状态变化,同时从localStorage初始化状态,避免页面刷新后状态丢失:
import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' // 全局注入,无需在模块中声明 }) export class AuthService { // BehaviorSubject可以保存当前值,新订阅者会立即获取最新值 private _loginStatus = new BehaviorSubject<boolean>(false); // 对外暴露可观察对象,避免外部直接修改状态 public loginStatus$: Observable<boolean> = this._loginStatus.asObservable(); constructor() { // 初始化时从localStorage读取登录状态 const isLoggedIn = !!localStorage.getItem('email'); this._loginStatus.next(isLoggedIn); } // 更新登录状态的方法 updateLoginStatus(isLoggedIn: boolean) { this._loginStatus.next(isLoggedIn); } }
2. 修改AppComponent,订阅服务状态
在AppComponent中注入AuthService,订阅状态变化来更新视图的显示逻辑:
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { AuthService } from './auth.service'; export class AppComponent implements OnInit { public isLoggedIn: boolean; constructor( private router: Router, private authService: AuthService ) { } ngOnInit() { // 订阅登录状态变化,同步到本地变量 this.authService.loginStatus$.subscribe(status => { this.isLoggedIn = status; }); } logout() { localStorage.removeItem('email'); this.router.navigate(['./login']); // 通知服务更新登出状态 this.authService.updateLoginStatus(false); } }
对应的AppComponent.html可以简化为:
<ul class="nav navbar-nav navbar-right"> <!-- 未登录时显示Login --> <li *ngIf="!isLoggedIn"> <a [routerLink]="['/login']">Login</a> </li> <!-- 已登录时显示Logout --> <li *ngIf="isLoggedIn"> <a (click)="logout()">LogOut</a> </li> </ul>
或者更简洁的方式,直接在模板中使用async管道(无需手动订阅和取消订阅):
<ul class="nav navbar-nav navbar-right"> <li *ngIf="!(authService.loginStatus$ | async)"> <a [routerLink]="['/login']">Login</a> </li> <li *ngIf="authService.loginStatus$ | async"> <a (click)="logout()">LogOut</a> </li> </ul>
这种方式下,AppComponent可以去掉isLoggedIn变量,减少代码量。
3. 修改LoginComponent,调用服务更新状态
在LoginComponent中注入AuthService,登录成功后通知服务更新状态:
import { Component, OnInit } from '@angular/core'; import { NgForm } from '@angular/forms'; import { Router } from '@angular/router'; import { AuthService } from './auth.service'; export class LoginComponent implements OnInit { public username: string; public password: string; constructor( private router: Router, private authService: AuthService ) { } ngOnInit() { } userLogin(form: NgForm) { if (form.value.username === "admin@gmail.com" && form.value.password === "admin") { localStorage.setItem('email', form.value.username); this.router.navigate(['./php']); // 通知服务更新登录状态 this.authService.updateLoginStatus(true); } } }
为什么原来的方法无效?
你之前的代码中,isValid是每个组件自己的实例变量,LoginComponent里的this.isValid和AppComponent的this.isValid完全是两个独立的变量,修改其中一个不会影响另一个。而且这两个组件是路由切换的关系,不是父子组件嵌套,所以@Input/@Output这种父子组件通讯方式也无法生效。
用共享服务的方式,我们把状态统一托管在服务中,所有需要使用状态的组件都订阅服务的可观察对象,这样状态变化时所有组件都会同步更新,完美解决跨组件状态共享的问题。
内容的提问来源于stack exchange,提问作者Apple Orange

