查看未定义的子组件
view child component undefined
我有一个父组件 (DepotSelectionComponent) 和一个子组件 (SiteDetailsComponent)。一个事件(moreDetails)被发送到父组件。然后此事件调用父级中的 getDetailsPage() 函数,该函数使用 ngswitch 更改页面并加载一些数据。但是,我尝试加载的组件似乎未定义,因此对该组件的引用无法通过 viewchild 装饰器工作。
我确定这与 ngswitch 有关,但我似乎无法弄清楚如何修复它,我尝试添加超时。 populateResults 函数工作是因为该组件已经被加载,但是 populateDepotResults 不工作是因为该组件未定义且尚未被开关加载。
父级(DepotSelectionComponent)html:
<div class="main">
<div [ngSwitch]="page">
<div *ngSwitchCase="'siteLandingPage'">
<div class="interactiveMap">
<app-interactive-map (siteDetailTable)="populateResults($event)"></app-interactive-map>
</div>
<div class="mapResult">
<app-map-result (moreDetails)="getDetailsPage($event)"></app-map-result>
</div>
</div>
<div *ngSwitchCase="'siteMoreDetails'">
<div>
<button mat-raised-button (click)="home()">Back</button>
</div>
<div class="clearFloat"></div>
<app-site-details [siteDetailsar]="siteDetailsar" (depotMoreDetails)="depotMoreDetails($event)"></app-site-details>
</div>
<div *ngSwitchCase="'depotMoreDetails'">
<div>
<button mat-raised-button (click)="getDetailsPage()">Back</button>
</div>
<div class="clearFloat"></div>
<app-depot-parent></app-depot-parent>
</div>
</div>
</div>
父级(DepotSelectionComponent)ts:
import { Component,
OnInit,
ViewChild,
ChangeDetectorRef } from '@angular/core';
import { MapResultComponent } from '../map-result/map-result.component';
import { SiteService } from '../shared/services/site-service';
import { siteDetails } from '../shared/interfaces/site-details.interface';
import { MatTableDataSource } from '@angular/material';
import { DepotService } from '../shared/services/depot-service';
import { depotDetails } from '../shared/interfaces/depot-details.interface';
import { SiteDetailsComponent } from '../site-details/site-details.component';
@Component({
selector: 'app-depot-selection',
templateUrl: './depot-selection.component.html',
styleUrls: ['./depot-selection.component.scss']
})
export class DepotSelectionComponent implements OnInit {
siteDetailsar: Array<{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
//depotResults: Array<{Depot_ID:number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
page:string;
countyName: string;
@ViewChild(MapResultComponent, {static: false})
private MapResultComponent: MapResultComponent;
@ViewChild(SiteDetailsComponent, {static: false})
private SiteDetailsComponent: SiteDetailsComponent;
constructor(
private _siteService: SiteService,
private _depotService: DepotService,
private changeDetectorRef: ChangeDetectorRef) { }
ngOnInit() {
this.page = "siteLandingPage";console.log('on init', this.SiteDetailsComponent);
// this returns undefined
}
ngAfterViewInit() {
console.log('on after view init', this.SiteDetailsComponent);
// this returns null
}
home() {
this.page = "siteLandingPage";
}
getDetailsPage(event) {
this.page = "siteMoreDetails";
var target = event.target || event.srcElement || event.currentTarget;
var idAttr = target.attributes.id;
this.getSiteDetailsByID(event.target.id);
this.populateDepotResults(event.target.id);
this.changeDetectorRef.detectChanges();
}
depotMoreDetails() {
this.page = "depotMoreDetails"
}
getSiteDetailsByID(id: number) {
this.siteDetailsar.length = 0;
this._siteService.getSiteByID(id)
.subscribe((data: any[]) => {
data.forEach(e => {
this.siteDetailsar.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
});
}
populateDepotResults(id: number) {
console.log(this.SiteDetailsComponent);
this.SiteDetailsComponent.depotResults.length = 0;
this._depotService.getAllDepots(id)
.subscribe((data: any[]) => {
data.forEach(e => {
this.SiteDetailsComponent.depotResults.push(new depotDetails(e.Depot_ID ,e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
this.SiteDetailsComponent.dataSource = new MatTableDataSource(this.SiteDetailsComponent.depotResults);
this.SiteDetailsComponent.dataSource.paginator = this.SiteDetailsComponent.paginator;
this.changeDetectorRef.detectChanges();
});
}
populateResults(countyName) {
this.MapResultComponent.mapResultHeader = countyName.dataObj.label;
this.MapResultComponent.siteResults.length = 0;
this._siteService.getSites(countyName.dataObj.label)
.subscribe((data: any[]) => {
data.forEach(e => {
this.MapResultComponent.siteResults.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
this.MapResultComponent.dataSource = new MatTableDataSource(this.MapResultComponent.siteResults);
this.MapResultComponent.dataSource.paginator = this.MapResultComponent.paginator;
this.changeDetectorRef.detectChanges();
});
}
}
child(SiteDetailsComponent) html:
<div class="siteInfo">
<div class="depotResultHeader">
<span>Site Information</span>
</div>
<mat-list>
<h3 mat-subheader>General Information</h3>
<mat-list-item >Site Name: {{ siteDetailsar[0].Name }} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Site Description: {{ siteDetailsar[0].Description }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Address1: {{ siteDetailsar[0].Address1 }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Address2: {{ siteDetailsar[0].Address2 }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Town: {{ siteDetailsar[0].Town }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>County: {{ siteDetailsar[0].County }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Postcode: {{ siteDetailsar[0].PostCode }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Telephone Number: {{ siteDetailsar[0].TelephoneNumber }}</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<div class="siteButtons">
<button mat-raised-button (click)="editSiteDialog()">Edit Site Details</button>
<button class="remove" mat-raised-button>Remove Site</button>
</div>
</div>
<div class="depotLocations">
<div class="depotResultHeader">
<span>List Of Depots</span>
</div>
<div class="mat-elevation-z8">
<mat-form-field class="filter">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Depot Name </th>
<td mat-cell *matCellDef="let element"> {{element.Name}}</td>
</ng-container>
<ng-container matColumnDef="address">
<th mat-header-cell *matHeaderCellDef> Address </th>
<td mat-cell *matCellDef="let element"> {{element.Address1}}</td>
</ng-container>
<ng-container matColumnDef="moreDetails">
<th mat-header-cell *matHeaderCellDef> More Details </th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button (click)="depotMoreDetailsPage($event)">More Details</button>
</td>
</ng-container>
<ng-container matColumnDef="remove">
<th mat-header-cell *matHeaderCellDef> Delete </th>
<td mat-cell *matCellDef="let element">
<button class="remove" mat-raised-button>Remove</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSize]="5" showFirstLastButtons></mat-paginator>
</div>
<div class="addNewDepot">
<button mat-raised-button>Add New Depot</button>
</div>
</div>
child(SiteDetailsComponent) ts:
import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
import { MatTableDataSource, MatPaginator, MatDialog, MatDialogConfig } from '@angular/material';
import { SiteInformationDialogComponent } from '../site-information-dialog/site-information-dialog.component';
import { SiteService } from '../shared/services/site-service';
import { siteDetails } from '../shared/interfaces/site-details.interface';
@Component({
selector: 'app-site-details',
templateUrl: './site-details.component.html',
styleUrls: ['./site-details.component.scss']
})
export class SiteDetailsComponent implements OnInit {
constructor(public dialog: MatDialog) { }
displayedColumns: string[] = ['name', 'address', 'moreDetails', 'remove'];
number: number;
result : string;
mapResults: Array<{name: string, town: string, address: string}> = [];
@Input() siteDetailsar: Array<{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
depotResults: Array<{Depot_ID: number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
dataSource : MatTableDataSource<any>;
@Output() depotMoreDetails = new EventEmitter();
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
ngOnInit() {
this.dataSource = new MatTableDataSource();
console.log(this.depotResults);
}
/**
* Set the paginator and sort after the view init since this component will
* be able to query its view for the initialized paginator and sort.
*/
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
editSiteDialog(){
const dialogConfig = new MatDialogConfig();
dialogConfig.autoFocus = true;
dialogConfig.disableClose = true;
this.dialog.open(SiteInformationDialogComponent, dialogConfig);
}
depotMoreDetailsPage(changed: string) {
this.depotMoreDetails.emit(changed);
}
}
export interface Data {
name: string;
town: string;
address: string;
telephone: string;
}
您没有在您的组件中实现接口 AfterViewInit
。
尝试这样实现:
...
export class DepotSelectionComponent implements OnInit, AfterViewInit {
...
和
...
export class SiteDetailsComponent implements OnInit, AfterViewInit {
...
先把@ViewChild()
改成@ViewChildren()
然后用QueryList<>
再订阅的变化。因此,只有在为组件正确加载模板时,您才能等待并加载组件..
export class DepotSelectionComponent implements AfterViewInit
{
@ViewChildren(SiteDetailsComponent ) childrenComponent: QueryList<SiteDetailsComponent >;
public ngAfterViewInit(): void
{
this.childrenComponent.changes.subscribe((comps: QueryList<SiteDetailsComponent>) =>
{
// Now you can access to the child component
});
}
}
要检查组件是否已加载,您必须首先将 @ViewChildren() 与 QueryList<> 一起使用,然后您必须使用 subscirbe() 来更改详细信息组件...
我有一个父组件 (DepotSelectionComponent) 和一个子组件 (SiteDetailsComponent)。一个事件(moreDetails)被发送到父组件。然后此事件调用父级中的 getDetailsPage() 函数,该函数使用 ngswitch 更改页面并加载一些数据。但是,我尝试加载的组件似乎未定义,因此对该组件的引用无法通过 viewchild 装饰器工作。
我确定这与 ngswitch 有关,但我似乎无法弄清楚如何修复它,我尝试添加超时。 populateResults 函数工作是因为该组件已经被加载,但是 populateDepotResults 不工作是因为该组件未定义且尚未被开关加载。
父级(DepotSelectionComponent)html:
<div class="main">
<div [ngSwitch]="page">
<div *ngSwitchCase="'siteLandingPage'">
<div class="interactiveMap">
<app-interactive-map (siteDetailTable)="populateResults($event)"></app-interactive-map>
</div>
<div class="mapResult">
<app-map-result (moreDetails)="getDetailsPage($event)"></app-map-result>
</div>
</div>
<div *ngSwitchCase="'siteMoreDetails'">
<div>
<button mat-raised-button (click)="home()">Back</button>
</div>
<div class="clearFloat"></div>
<app-site-details [siteDetailsar]="siteDetailsar" (depotMoreDetails)="depotMoreDetails($event)"></app-site-details>
</div>
<div *ngSwitchCase="'depotMoreDetails'">
<div>
<button mat-raised-button (click)="getDetailsPage()">Back</button>
</div>
<div class="clearFloat"></div>
<app-depot-parent></app-depot-parent>
</div>
</div>
</div>
父级(DepotSelectionComponent)ts:
import { Component,
OnInit,
ViewChild,
ChangeDetectorRef } from '@angular/core';
import { MapResultComponent } from '../map-result/map-result.component';
import { SiteService } from '../shared/services/site-service';
import { siteDetails } from '../shared/interfaces/site-details.interface';
import { MatTableDataSource } from '@angular/material';
import { DepotService } from '../shared/services/depot-service';
import { depotDetails } from '../shared/interfaces/depot-details.interface';
import { SiteDetailsComponent } from '../site-details/site-details.component';
@Component({
selector: 'app-depot-selection',
templateUrl: './depot-selection.component.html',
styleUrls: ['./depot-selection.component.scss']
})
export class DepotSelectionComponent implements OnInit {
siteDetailsar: Array<{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
//depotResults: Array<{Depot_ID:number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
page:string;
countyName: string;
@ViewChild(MapResultComponent, {static: false})
private MapResultComponent: MapResultComponent;
@ViewChild(SiteDetailsComponent, {static: false})
private SiteDetailsComponent: SiteDetailsComponent;
constructor(
private _siteService: SiteService,
private _depotService: DepotService,
private changeDetectorRef: ChangeDetectorRef) { }
ngOnInit() {
this.page = "siteLandingPage";console.log('on init', this.SiteDetailsComponent);
// this returns undefined
}
ngAfterViewInit() {
console.log('on after view init', this.SiteDetailsComponent);
// this returns null
}
home() {
this.page = "siteLandingPage";
}
getDetailsPage(event) {
this.page = "siteMoreDetails";
var target = event.target || event.srcElement || event.currentTarget;
var idAttr = target.attributes.id;
this.getSiteDetailsByID(event.target.id);
this.populateDepotResults(event.target.id);
this.changeDetectorRef.detectChanges();
}
depotMoreDetails() {
this.page = "depotMoreDetails"
}
getSiteDetailsByID(id: number) {
this.siteDetailsar.length = 0;
this._siteService.getSiteByID(id)
.subscribe((data: any[]) => {
data.forEach(e => {
this.siteDetailsar.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
});
}
populateDepotResults(id: number) {
console.log(this.SiteDetailsComponent);
this.SiteDetailsComponent.depotResults.length = 0;
this._depotService.getAllDepots(id)
.subscribe((data: any[]) => {
data.forEach(e => {
this.SiteDetailsComponent.depotResults.push(new depotDetails(e.Depot_ID ,e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
this.SiteDetailsComponent.dataSource = new MatTableDataSource(this.SiteDetailsComponent.depotResults);
this.SiteDetailsComponent.dataSource.paginator = this.SiteDetailsComponent.paginator;
this.changeDetectorRef.detectChanges();
});
}
populateResults(countyName) {
this.MapResultComponent.mapResultHeader = countyName.dataObj.label;
this.MapResultComponent.siteResults.length = 0;
this._siteService.getSites(countyName.dataObj.label)
.subscribe((data: any[]) => {
data.forEach(e => {
this.MapResultComponent.siteResults.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
})
this.MapResultComponent.dataSource = new MatTableDataSource(this.MapResultComponent.siteResults);
this.MapResultComponent.dataSource.paginator = this.MapResultComponent.paginator;
this.changeDetectorRef.detectChanges();
});
}
}
child(SiteDetailsComponent) html:
<div class="siteInfo">
<div class="depotResultHeader">
<span>Site Information</span>
</div>
<mat-list>
<h3 mat-subheader>General Information</h3>
<mat-list-item >Site Name: {{ siteDetailsar[0].Name }} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Site Description: {{ siteDetailsar[0].Description }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Address1: {{ siteDetailsar[0].Address1 }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Address2: {{ siteDetailsar[0].Address2 }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Town: {{ siteDetailsar[0].Town }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>County: {{ siteDetailsar[0].County }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Postcode: {{ siteDetailsar[0].PostCode }}</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Telephone Number: {{ siteDetailsar[0].TelephoneNumber }}</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<div class="siteButtons">
<button mat-raised-button (click)="editSiteDialog()">Edit Site Details</button>
<button class="remove" mat-raised-button>Remove Site</button>
</div>
</div>
<div class="depotLocations">
<div class="depotResultHeader">
<span>List Of Depots</span>
</div>
<div class="mat-elevation-z8">
<mat-form-field class="filter">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Depot Name </th>
<td mat-cell *matCellDef="let element"> {{element.Name}}</td>
</ng-container>
<ng-container matColumnDef="address">
<th mat-header-cell *matHeaderCellDef> Address </th>
<td mat-cell *matCellDef="let element"> {{element.Address1}}</td>
</ng-container>
<ng-container matColumnDef="moreDetails">
<th mat-header-cell *matHeaderCellDef> More Details </th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button (click)="depotMoreDetailsPage($event)">More Details</button>
</td>
</ng-container>
<ng-container matColumnDef="remove">
<th mat-header-cell *matHeaderCellDef> Delete </th>
<td mat-cell *matCellDef="let element">
<button class="remove" mat-raised-button>Remove</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSize]="5" showFirstLastButtons></mat-paginator>
</div>
<div class="addNewDepot">
<button mat-raised-button>Add New Depot</button>
</div>
</div>
child(SiteDetailsComponent) ts:
import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
import { MatTableDataSource, MatPaginator, MatDialog, MatDialogConfig } from '@angular/material';
import { SiteInformationDialogComponent } from '../site-information-dialog/site-information-dialog.component';
import { SiteService } from '../shared/services/site-service';
import { siteDetails } from '../shared/interfaces/site-details.interface';
@Component({
selector: 'app-site-details',
templateUrl: './site-details.component.html',
styleUrls: ['./site-details.component.scss']
})
export class SiteDetailsComponent implements OnInit {
constructor(public dialog: MatDialog) { }
displayedColumns: string[] = ['name', 'address', 'moreDetails', 'remove'];
number: number;
result : string;
mapResults: Array<{name: string, town: string, address: string}> = [];
@Input() siteDetailsar: Array<{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
depotResults: Array<{Depot_ID: number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];
dataSource : MatTableDataSource<any>;
@Output() depotMoreDetails = new EventEmitter();
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
ngOnInit() {
this.dataSource = new MatTableDataSource();
console.log(this.depotResults);
}
/**
* Set the paginator and sort after the view init since this component will
* be able to query its view for the initialized paginator and sort.
*/
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
editSiteDialog(){
const dialogConfig = new MatDialogConfig();
dialogConfig.autoFocus = true;
dialogConfig.disableClose = true;
this.dialog.open(SiteInformationDialogComponent, dialogConfig);
}
depotMoreDetailsPage(changed: string) {
this.depotMoreDetails.emit(changed);
}
}
export interface Data {
name: string;
town: string;
address: string;
telephone: string;
}
您没有在您的组件中实现接口 AfterViewInit
。
尝试这样实现:
...
export class DepotSelectionComponent implements OnInit, AfterViewInit {
...
和
...
export class SiteDetailsComponent implements OnInit, AfterViewInit {
...
先把@ViewChild()
改成@ViewChildren()
然后用QueryList<>
再订阅的变化。因此,只有在为组件正确加载模板时,您才能等待并加载组件..
export class DepotSelectionComponent implements AfterViewInit
{
@ViewChildren(SiteDetailsComponent ) childrenComponent: QueryList<SiteDetailsComponent >;
public ngAfterViewInit(): void
{
this.childrenComponent.changes.subscribe((comps: QueryList<SiteDetailsComponent>) =>
{
// Now you can access to the child component
});
}
}
要检查组件是否已加载,您必须首先将 @ViewChildren() 与 QueryList<> 一起使用,然后您必须使用 subscirbe() 来更改详细信息组件...