Angular使用@ViewChild共享组件布尔属性时ngAfterViewInit报undefined错误
问题原因及修复方案
报错根因
enableLoginForm是app.component中ngAfterViewInit阶段的undefined属性
这个报错是多个问题共同导致的:
- 子组件
<redirect-user-to-mobile-app>受模板中*ngIf="!isLoading && isMobileDevice() && !isLogged"条件控制渲染,ngAfterViewInit执行时如果条件不满足,子组件还未渲染,@ViewChild获取到的redirectComponent就是undefined,访问它的属性自然报错。 AppComponent中没有声明enableLoginForm属性,模板直接使用该变量也会出现未定义报错。- 子组件的
enableLoginForm是内部状态,变化时父组件无法自动感知,即使通过ViewChild拿到子组件实例,也不能响应后续的状态更新。 - 原
RedirectUserToMobileAppComponent代码中使用了this.router.navigate但构造函数没有注入Router,运行时也会报错。
修复步骤
1. 修改子组件RedirectUserToMobileAppComponent,新增输出事件抛出状态变化
import { Component, OnInit, Output, EventEmitter, } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'redirect-user-to-mobile-app', templateUrl: './redirect-user-to-mobile-app.component.html', styleUrls: ['./redirect-user-to-mobile-app.component.sass'], }) export class RedirectUserToMobileAppComponent implements OnInit { // 新增输出事件 @Output() loginFormEnable = new EventEmitter<boolean>(); enableLoginForm = false; // 注入Router constructor(private router: Router) {} ngOnInit(): void {} OnLogin(): void { this.enableLoginForm = true; // 状态变化时抛出事件 this.loginFormEnable.emit(true); this.router.navigate(['../login']); } }
2. 修改父组件AppComponent,声明对应属性接收状态
import { Component, HostListener, OnDestroy, OnInit, AfterViewInit, } from '@angular/core'; import { MatIconRegistry } from '@angular/material/icon'; import { DomSanitizer } from '@angular/platform-browser'; import { FirebaseService } from './services/firebase/firebase.service'; import { SnakeMessage, SnakeMessageService, } from './services/snakeMessage/snakeMessage.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Subscription } from 'rxjs'; import { StorageService } from './services/storage/storage.service'; import { AuthService } from './services/auth/auth.service'; @Component({ selector: 'app-component', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) export class AppComponent implements OnInit, OnDestroy, AfterViewInit { favIcon: HTMLLinkElement = document.querySelector('#appIcon'); private snakeMessageSub: Subscription; isLoading = true; isLogged: boolean; // 新增属性存储登录表单开关状态 enableLoginForm = false; constructor( private matIconRegistry: MatIconRegistry, private firebaseService: FirebaseService, private snakeMessageService: SnakeMessageService, private _snackBar: MatSnackBar, private storageService: StorageService, private domSanitizer: DomSanitizer, private authService: AuthService ) { this.registerCustomIcons(); this.storageService.initDB(); this.storageService.onLoaded$.subscribe((loaded) => { if (loaded) { this.isLoading = false; } }); this.isLogged = this.authService.isLoggedIn; } // 新增方法接收子组件抛出的状态 onLoginFormEnable(status: boolean) { this.enableLoginForm = status; } ngAfterViewInit() { // 删掉原来访问redirectComponent的代码,不再需要 } ngOnInit(): void { this.snakeMessageSub = this.snakeMessageService.messageSub.subscribe( (snakeMessage: SnakeMessage) => { this._snackBar.open(snakeMessage.message, snakeMessage.action, { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top', }); } ); } // 原代码缺失的registerCustomIcons、ngOnDestroy方法请自行保留 }
3. 修改app.component.html模板,绑定子组件输出事件
<ng-container *ngIf="!isLoading"> <ng-container *ngIf="isMobileDevice() && !isLogged"> <!-- 绑定子组件的输出事件 --> <redirect-user-to-mobile-app (loginFormEnable)="onLoginFormEnable($event)"> </redirect-user-to-mobile-app> <router-outlet *ngIf="enableLoginForm" ></router-outlet> </ng-container> <router-outlet *ngIf="!isMobileDevice()"></router-outlet> </ng-container>
内容的提问来源于stack exchange,提问作者Wahéb
相关产品推荐
相关产品推荐

