使用*ngIf结合!与async管道处理BehaviorSubject时触发类型错误
问题:Angular中*ngIf结合!async管道触发TS2769类型错误的解决方法
我基于BehaviorSubject实现了登录状态服务,用于跟踪用户是否登录。想在组件模板中实现用户登录时隐藏注册按钮,使用*ngIf="!getLoginStatus() | async"却触发TS2769类型错误;移除!运算符后代码可正常运行,目前没有ngNotIf指令,请问该如何解决?
相关代码
login.service.ts
constructor(private http: HttpClient){ this.loggedSubject = new BehaviorSubject<boolean>(false); } getLoginStatus(){ return this.loggedSubject.asObservable(); }
login.component.html
<button mat-button class="menu-button" routerLink="/register" (click)="sidenav.toggle()" *ngIf="!getLoginStatus() | async"> <mat-icon>key</mat-icon> <span>Register</span> </button>
报错信息
ERROR src/app/header/header.component.html:33:102 - error TS2769: No overload matches this call. Overload 1 of 3, '(obj: Observable<unknown> | Subscribable<unknown> | Promise<unknown>): unknown', gave the following error. Argument of type 'boolean' is not assignable to parameter of type 'Observable<unknown> | Subscribable<unknown> | Promise<unknown>'. Overload 2 of 3, '(obj: null | undefined): null', gave the following error. Argument of type 'false' is not assignable to parameter of type 'null | undefined'. Overload 3 of 3, '(obj: Observable<unknown> | Subscribable<unknown> | Promise<unknown> | null | undefined): unknown', gave the following error. Argument of type 'false' is not assignable to parameter of type 'Observable<unknown> | Subscribable<unknown> | Promise<unknown> | null | undefined'. 33 <button mat-button class="menu-button" routerLink="/register" (click)="sidenav.toggle()" *ngIf="!getLoginStatus() | async"> ~~~~~~~~~~~~~~~~~ src/app/header/header.component.ts:21:16 21 templateUrl: './header.component.html', ~~~~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component HeaderComponent.
错误原因
核心问题是运算符优先级:!的优先级高于|,代码会先执行!getLoginStatus(),将Observable对象转为布尔值(永远为false,因为对象非空),再把这个布尔值传给async管道。而async管道仅接受Observable/Promise等异步类型,因此触发类型不匹配错误。
解决方法
方法1:给async管道结果加括号后取反
通过括号强制先执行async管道,再对结果取反:
<button mat-button class="menu-button" routerLink="/register" (click)="sidenav.toggle()" *ngIf="!(getLoginStatus() | async)"> <mat-icon>key</mat-icon> <span>Register</span> </button>
方法2:在服务中新增反向状态的Observable
如果多个组件需要用到“未登录”状态,可以在登录服务中新增方法,直接返回取反后的Observable:
// login.service.ts getIsNotLoggedIn(): Observable<boolean> { return this.loggedSubject.asObservable().pipe(map(isLoggedIn => !isLoggedIn)); }
模板中直接调用该方法:
<button mat-button class="menu-button" routerLink="/register" (click)="sidenav.toggle()" *ngIf="getIsNotLoggedIn() | async"> <mat-icon>key</mat-icon> <span>Register</span> </button>
额外优化:避免模板频繁调用方法
模板每次变更检测都会调用getLoginStatus(),建议在组件类中将Observable赋值为属性,减少不必要的方法调用:
// header.component.ts loginStatus$: Observable<boolean>; constructor(private loginService: LoginService) { this.loginStatus$ = this.loginService.getLoginStatus(); }
模板中直接使用属性:
<button mat-button class="menu-button" routerLink="/register" (click)="sidenav.toggle()" *ngIf="!(loginStatus$ | async)"> <mat-icon>key</mat-icon> <span>Register</span> </button>
内容的提问来源于stack exchange,提问作者Henry McJonathan
相关产品推荐
相关产品推荐

