0

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:

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

parent(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;
}
Edric
  • 24,639
  • 13
  • 81
  • 91
ashley g
  • 857
  • 3
  • 11
  • 21
  • Can you explain in short ? what is not working? – Developer Jan 06 '20 at 12:04
  • I am getting a TypeError: Cannot read property 'depotResults' of undefined. – ashley g Jan 06 '20 at 12:06
  • Change name of property `SiteDetailsComponent` with `siteDetailsComponent` with any-name. so your `@ViewChild(SiteDetailsComponent, {static: false}) private siteDetailsComponent: SiteDetailsComponent;` – Developer Jan 06 '20 at 12:13
  • same issue, i am almost positive it has something to do with the component not being loaded – ashley g Jan 06 '20 at 12:18
  • initially you are loading `` component with `[ngSwitch]` so where is `SiteDetailsComponent` in template? – Developer Jan 06 '20 at 12:27
  • gets loaded when the (moreDetails) event gets emitted. it is in the *ngSwitchCase 'siteMoreDetails' . Maybe we can continue this in chat – ashley g Jan 06 '20 at 12:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205442/discussion-between-ashley-g-and-gaurangdhorda). – ashley g Jan 06 '20 at 12:33

2 Answers2

1

First change @ViewChild() to @ViewChildren() and use QueryList<> 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<SiteDetailsComponent >;

    public ngAfterViewInit(): void
    { 
        this.childrenComponent.changes.subscribe((comps: QueryList<SiteDetailsComponent>) =>
        {
              // 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...

Developer
  • 3,309
  • 2
  • 19
  • 23
-1

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 {
...