当我使用 @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>
我有一个名为 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>