查看子组件未定义。

huangapple go评论77阅读模式
英文:

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 }> = [];
  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();
      });
  }
}

子组件(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: {{

<details>
<summary>英文:</summary>

I have a parent component(DepotSelectionComponent) and a child component(SiteDetailsComponent). An event(moreDetails) is emitted to the parent component. This event then calls the getDetailsPage()function within the parent, which changes the page using ngswitch and also loads some data. However the component what i am trying to load seems to be undefined therefore the references to that component are not working via the viewchild decorator.

I am sure this has something to do with the ngswitch however i just cant seem to figure out how to fix it, i have tried adding timeouts. The populateResults function works due to that component already being loaded however the populateDepotResults dont work due to the component being undefined and not loaded by the switch yet.

parent(DepotSelectionComponent) html:


        &lt;div class=&quot;main&quot;&gt;  
    
        &lt;div [ngSwitch]=&quot;page&quot;&gt;
    
            &lt;div *ngSwitchCase=&quot;&#39;siteLandingPage&#39;&quot;&gt;
                &lt;div class=&quot;interactiveMap&quot;&gt;
                    &lt;app-interactive-map (siteDetailTable)=&quot;populateResults($event)&quot;&gt;&lt;/app-interactive-map&gt;
                &lt;/div&gt;
                &lt;div class=&quot;mapResult&quot;&gt;
                    &lt;app-map-result (moreDetails)=&quot;getDetailsPage($event)&quot;&gt;&lt;/app-map-result&gt;
                &lt;/div&gt;
            &lt;/div&gt;
    
            &lt;div *ngSwitchCase=&quot;&#39;siteMoreDetails&#39;&quot;&gt;
                &lt;div&gt;
                    &lt;button mat-raised-button (click)=&quot;home()&quot;&gt;Back&lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;clearFloat&quot;&gt;&lt;/div&gt;
                &lt;app-site-details [siteDetailsar]=&quot;siteDetailsar&quot; (depotMoreDetails)=&quot;depotMoreDetails($event)&quot;&gt;&lt;/app-site-details&gt;
            &lt;/div&gt;
    
            &lt;div *ngSwitchCase=&quot;&#39;depotMoreDetails&#39;&quot;&gt;
                &lt;div&gt;
                    &lt;button mat-raised-button (click)=&quot;getDetailsPage()&quot;&gt;Back&lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;clearFloat&quot;&gt;&lt;/div&gt;
                &lt;app-depot-parent&gt;&lt;/app-depot-parent&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        
    &lt;/div&gt;


parent(DepotSelectionComponent) ts:

        import {  Component, 
              OnInit,
              ViewChild, 
              ChangeDetectorRef } from &#39;@angular/core&#39;;
    import { MapResultComponent } from &#39;../map-result/map-result.component&#39;;
    import { SiteService } from &#39;../shared/services/site-service&#39;;
    import { siteDetails } from &#39;../shared/interfaces/site-details.interface&#39;;
    import { MatTableDataSource } from &#39;@angular/material&#39;;
    import { DepotService } from &#39;../shared/services/depot-service&#39;;
    import { depotDetails } from &#39;../shared/interfaces/depot-details.interface&#39;;
    import { SiteDetailsComponent } from &#39;../site-details/site-details.component&#39;;
    
    @Component({
      selector: &#39;app-depot-selection&#39;,
      templateUrl: &#39;./depot-selection.component.html&#39;,
      styleUrls: [&#39;./depot-selection.component.scss&#39;]
    })
    export class DepotSelectionComponent implements OnInit {
      siteDetailsar: Array&lt;{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}&gt; = [];
      //depotResults: Array&lt;{Depot_ID:number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}&gt; = [];
    
      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 = &quot;siteLandingPage&quot;;console.log(&#39;on init&#39;, this.SiteDetailsComponent);
        // this returns undefined
      }
    
      ngAfterViewInit() {
          console.log(&#39;on after view init&#39;, this.SiteDetailsComponent);
          // this returns null
      }
    
      home() {
        this.page = &quot;siteLandingPage&quot;;
      }
    
      getDetailsPage(event) {
        this.page = &quot;siteMoreDetails&quot;;
        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 = &quot;depotMoreDetails&quot;
      }
    
     getSiteDetailsByID(id: number) {
        this.siteDetailsar.length = 0;
        this._siteService.getSiteByID(id)
        .subscribe((data: any[]) =&gt; {
          data.forEach(e =&gt; {
            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[]) =&gt; {
          data.forEach(e =&gt; {
            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[]) =&gt; {
          data.forEach(e =&gt; {
          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:

    &lt;div class=&quot;siteInfo&quot;&gt;
        &lt;div class=&quot;depotResultHeader&quot;&gt;
            &lt;span&gt;Site Information&lt;/span&gt;
        &lt;/div&gt;
        &lt;mat-list&gt;
            &lt;h3 mat-subheader&gt;General Information&lt;/h3&gt;
            &lt;mat-list-item &gt;Site Name: {{ siteDetailsar[0].Name }} &lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Site Description: {{ siteDetailsar[0].Description }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Address1: {{ siteDetailsar[0].Address1 }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Address2: {{ siteDetailsar[0].Address2 }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Town: {{ siteDetailsar[0].Town }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;County: {{ siteDetailsar[0].County }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Postcode: {{ siteDetailsar[0].PostCode }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
            &lt;mat-list-item&gt;Telephone Number: {{ siteDetailsar[0].TelephoneNumber }}&lt;/mat-list-item&gt;
            &lt;mat-divider&gt;&lt;/mat-divider&gt;
        &lt;/mat-list&gt;
    
        &lt;div class=&quot;siteButtons&quot;&gt;    
            &lt;button mat-raised-button (click)=&quot;editSiteDialog()&quot;&gt;Edit Site Details&lt;/button&gt;
            &lt;button class=&quot;remove&quot; mat-raised-button&gt;Remove Site&lt;/button&gt;
        &lt;/div&gt;
        
    &lt;/div&gt;
    
    &lt;div class=&quot;depotLocations&quot;&gt;
        &lt;div class=&quot;depotResultHeader&quot;&gt;
            &lt;span&gt;List Of Depots&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mat-elevation-z8&quot;&gt;
            &lt;mat-form-field class=&quot;filter&quot;&gt;
                &lt;input matInput (keyup)=&quot;applyFilter($event.target.value)&quot; placeholder=&quot;Filter&quot;&gt;
            &lt;/mat-form-field&gt;
    
            &lt;table mat-table [dataSource]=&quot;dataSource&quot;&gt;
        
            &lt;ng-container matColumnDef=&quot;name&quot;&gt;
                &lt;th mat-header-cell *matHeaderCellDef&gt; Depot Name &lt;/th&gt;
                &lt;td mat-cell *matCellDef=&quot;let element&quot;&gt; {{element.Name}}&lt;/td&gt;
            &lt;/ng-container&gt;
        
            &lt;ng-container matColumnDef=&quot;address&quot;&gt;
                &lt;th mat-header-cell *matHeaderCellDef&gt; Address &lt;/th&gt;
                &lt;td mat-cell *matCellDef=&quot;let element&quot;&gt; {{element.Address1}}&lt;/td&gt;
            &lt;/ng-container&gt;
    
            &lt;ng-container matColumnDef=&quot;moreDetails&quot;&gt;
                &lt;th mat-header-cell *matHeaderCellDef&gt; More Details &lt;/th&gt;
                &lt;td mat-cell *matCellDef=&quot;let element&quot;&gt; 
                    &lt;button mat-raised-button (click)=&quot;depotMoreDetailsPage($event)&quot;&gt;More Details&lt;/button&gt; 
                &lt;/td&gt;
            &lt;/ng-container&gt;
    
            &lt;ng-container matColumnDef=&quot;remove&quot;&gt;
                &lt;th mat-header-cell *matHeaderCellDef&gt; Delete &lt;/th&gt;
                &lt;td mat-cell *matCellDef=&quot;let element&quot;&gt; 
                    &lt;button class=&quot;remove&quot; mat-raised-button&gt;Remove&lt;/button&gt; 
                &lt;/td&gt;
            &lt;/ng-container&gt;
    
            &lt;tr mat-header-row *matHeaderRowDef=&quot;displayedColumns&quot;&gt;&lt;/tr&gt;
            &lt;tr mat-row *matRowDef=&quot;let row; columns: displayedColumns;&quot;&gt;&lt;/tr&gt;
            &lt;/table&gt;
        
            &lt;mat-paginator [pageSize]=&quot;5&quot; showFirstLastButtons&gt;&lt;/mat-paginator&gt;
        &lt;/div&gt;
    
        &lt;div class=&quot;addNewDepot&quot;&gt;
            &lt;button mat-raised-button&gt;Add New Depot&lt;/button&gt;
        &lt;/div&gt;
    &lt;/div&gt;

child(SiteDetailsComponent) ts:

    import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from &#39;@angular/core&#39;;
    import { MatTableDataSource, MatPaginator, MatDialog, MatDialogConfig } from &#39;@angular/material&#39;;
    import { SiteInformationDialogComponent } from &#39;../site-information-dialog/site-information-dialog.component&#39;;
    import { SiteService } from &#39;../shared/services/site-service&#39;;
    import { siteDetails } from &#39;../shared/interfaces/site-details.interface&#39;;
    
    @Component({
      selector: &#39;app-site-details&#39;,
      templateUrl: &#39;./site-details.component.html&#39;,
      styleUrls: [&#39;./site-details.component.scss&#39;]
    })
    export class SiteDetailsComponent implements OnInit {
    
      constructor(public dialog: MatDialog) { }
    
      displayedColumns: string[] = [&#39;name&#39;, &#39;address&#39;, &#39;moreDetails&#39;, &#39;remove&#39;];
      number: number;
      result : string;
      mapResults: Array&lt;{name: string, town: string, address: string}&gt; = [];
      @Input() siteDetailsar: Array&lt;{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}&gt; = [];
      depotResults: Array&lt;{Depot_ID: number, Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}&gt; = [];
      dataSource : MatTableDataSource&lt;any&gt;;
    
      @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;
    }





</details>


# 答案1
**得分**: 1

首先将`@ViewChild()`更改为`@ViewChildren()`,并使用`QueryList&lt;&gt;`,然后订阅变化。这样,只有在模板正确加载了组件后,您才能等待并加载组件。

```typescript
export class DepotSelectionComponent implements AfterViewInit {
  
  @ViewChildren(SiteDetailsComponent) childrenComponent: QueryList<SiteDetailsComponent>;

  public ngAfterViewInit(): void { 
    this.childrenComponent.changes.subscribe((comps: QueryList<SiteDetailsComponent>) => {
      // 现在您可以访问子组件
    });
  }
}

要检查组件是否已加载或未加载,您首先必须使用@ViewChildren()QueryList&lt;&gt;,然后您必须订阅changes以侦听详细组件的变化。

英文:

First change @ViewChild() to @ViewChildren() and use QueryList&lt;&gt; and then subscribe to changes of. so, you can get wait and load component only when is template loaded properly for component..

export class DepotSelectionComponent implements AfterViewInit  
{
@ViewChildren(SiteDetailsComponent ) childrenComponent: QueryList&lt;SiteDetailsComponent &gt;;
public ngAfterViewInit(): void
{ 
this.childrenComponent.changes.subscribe((comps: QueryList&lt;SiteDetailsComponent&gt;) =&gt;
{
// Now you can access to the child component
});
}
}

To checking if component is loaded or not you have to first use @ViewChildren() with QueryList<> and for then you have to subscirbe() for changes in details-component...

答案2

得分: -1

你没有在你的组件中实现接口AfterViewInit

尝试像这样实现:

...
export class DepotSelectionComponent implements OnInit, AfterViewInit {
...
和
...
export class SiteDetailsComponent implements OnInit, AfterViewInit {
...
英文:

You are not implementing the interface AfterViewInit in your components.

Try to implement like this:

...
export class DepotSelectionComponent implements OnInit, AfterViewInit {
...

and

...
export class SiteDetailsComponent implements OnInit, AfterViewInit {
...

huangapple
  • 本文由 发表于 2020年1月6日 19:57:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/59611693.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定