查看未定义的子组件

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() 来更改详细信息组件...