当我使用 @ViewChild 在 Angular 组件之间共享布尔数据时,我得到 undefined 属性

I get undefined property when I share boolean data between Angular components using @ViewChild

我有一个名为 RedirectUserToMobileAppComponent 的组件,我想与 app.component 共享一个名为 enableLoginForm 的布尔值 属性。 执行时,出现此错误:

enableLoginForm is undefined property on ngAfterViewInit in app.component

这是 RedirectUserToMobileAppComponent 组件:

    import {
        Component,
        ComponentFactoryResolver,
        ComponentRef,
        Inject,
        Input,
        OnInit,
        Output,
        ViewChild,
        ViewContainerRef,
    } from '@angular/core';
    import { Observable, Subscription } from 'rxjs';
    import { filter, map, pluck, tap } from 'rxjs/operators';
    import { ActivatedRoute, Router } from '@angular/router';
    import { MAT_DIALOG_SCROLL_STRATEGY_FACTORY } from '@angular/material/dialog';

    @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 {
        constructor(
        ) {}
        enableLoginForm = false;
        ngOnInit(): void {}
    
        OnLogin(): void {
            this.enableLoginForm = true;
            this.router.navigate(['../login']);
        }
        
    }

这是 app.component:

    import {
    Component,
    HostListener,
    OnDestroy,
    OnInit,
    ViewChild,
    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';
import { RedirectUserToMobileAppComponent } from './redirect-user-to-mobile-app/redirect-user-to-mobile-app.component';

@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;

    @ViewChild(RedirectUserToMobileAppComponent)
    redirectComponent!: RedirectUserToMobileAppComponent;

    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;
    }

    ngAfterViewInit() {
        if (this.redirectComponent.enableLoginForm) {
            this._is = this.redirectComponent.enableLoginForm;
        }
    }

    ngOnInit(): void {
        this.snakeMessageSub = this.snakeMessageService.messageSub.subscribe(
            (snakeMessage: SnakeMessage) => {
                this._snackBar.open(snakeMessage.message, snakeMessage.action, {
                    duration: 3000,
                    horizontalPosition: 'center',
                    verticalPosition: 'top',
                });
            }
        );
    }

这是我的app.component.html

<ng-container *ngIf="!isLoading">
<ng-container *ngIf="isMobileDevice() && !isLogged">
    <redirect-user-to-mobile-app> </redirect-user-to-mobile-app>
    <router-outlet
        *ngIf="enableLoginForm"
    ></router-outlet>
</ng-container>

<router-outlet *ngIf="!isMobileDevice()"></router-outlet>

ViewChild returns 对 HTML 元素的引用.

我会引用angular.io:

Property decorator that configures a view query. The change detector looks for the first element or the directive matching the selector in the view DOM. If the view DOM changes, and a new child matches the selector, the property is updated.

所以你不能用ViewChild访问它的控制器变量。

我的建议是使用 service 来传递数据。

这是您使用 ViewChild 的方式:

@ViewChild('templateId', { static: false }) redirectComponent: RedirectUserToMobileAppComponent;

您应该在模板部分设置 templateId :

<redirect-user-to-mobile-app #templateId> ... </redirect-user-to-mobile-app>

编辑:虽然我同意 skyBlue,但您应该使用服务在组件之间共享数据

我已经改变了方法,我使用了@Output() 组件并且它工作正常:

这是更改方法后的RedirectUserToMobileAppComponent组件:

    import {
    Component,
    ComponentFactoryResolver,
    ComponentRef,
    Inject,
    Input,
    OnChanges,
    OnInit,
    Output,
    SimpleChanges,
    ViewChild,
    ViewContainerRef,
    EventEmitter,
} from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { filter, map, pluck, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { MAT_DIALOG_SCROLL_STRATEGY_FACTORY } from '@angular/material/dialog';
@Component({
    selector: 'yobi-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 {
    constructor(
        private router: Router,
        private componentFactoryResolver: ComponentFactoryResolver
    ) {}

    @Output() _enableLoginForm: EventEmitter<boolean> = new EventEmitter();
    variable: any;
    login: boolean;
    enableLoginForm = false;
    enableSignupForm = false;
    ngOnInit(): void {}

    sendDataToParent() {
        this.enableLoginForm = true;
        this._enableLoginForm.emit(this.enableLoginForm);
        console.log(this.enableLoginForm + ' From redirect ');
    }

我将此添加到 RedirectUserToMobileAppComponent.html:

<a class="login-text" (click)="sendDataToParent()">
          Login
      </a>

我将此代码添加到 app.component :

receiveChildData($event) {
    this.enableLoginForm = $event;
}

我将此代码添加到 app.component.html :

<redirect-user-to-mobile-app
        (_enableLoginForm)="receiveChildData($event)"
    >
    </redirect-user-to-mobile-app>