I created a simple paginator component that I use all over my app. I am using view child to access some of the properties; however I encountered a weird bug with View Child.
When I use the component class name (PaginatorComponent) as the selector for view child, it is undefined. However, when I switch to a template reference variable everything works as expected. The bug only shows up in the deployed version of my app: everything worked fine locally (even in production mode) and I haven't been able to replicate it in a Stackblitz.
I am using Angular and Angular Material, and the deployed app is running with other micro-frontends using single-spa/angular.
I have searched and searched but can find nothing helpful. Does anyone know of a nuance in Angular / View Child that would cause this issue? I am able to get around the issue by using template reference variables, but it bothers me that I cannot figure out why using the class name doesn't work.
Paginator ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { paginationConfigs } from 'configs';
export interface PageChangeEvent {
pageNumber: string;
pageLength: string;
}
@Component({
selector: 'app-paginator',
templateUrl: './paginator.component.html',
styleUrls: ['./paginator.component.css'],
})
export class PaginatorComponent implements OnInit {
@Input() viewingLastPage: boolean;
public _pageIndex = 0;
get pageIndex(): number {
return this._pageIndex;
}
set pageIndex(val: number) {
this._pageIndex = val;
this.pageNumberChange.emit({
pageLength: this.pageLength,
pageNumber: val.toString(),
});
}
public _pageLength = paginationConfigs.pageLengthOptions[0];
get pageLength(): string {
return this._pageLength;
}
set pageLength(val) {
this._pageLength = val;
this.pageLengthChange.emit({
pageLength: val,
pageNumber: this.pageIndex.toString(),
});
}
public pageLengthOptions: string[] = paginationConfigs.pageLengthOptions;
@Output() pageNumberChange: EventEmitter<PageChangeEvent> = new EventEmitter<
PageChangeEvent
>();
@Output() pageLengthChange: EventEmitter<PageChangeEvent> = new EventEmitter<
PageChangeEvent
>();
@Output() initialized: EventEmitter<PageChangeEvent> = new EventEmitter<PageChangeEvent>();
constructor() {}
ngOnInit(): void {
this.emitInitialized();
}
changePage(change: number): void {
this.pageIndex += change;
}
emitPageNumberChange(): void {
this.pageNumberChange.emit({
pageNumber: this.pageIndex.toString(),
pageLength: this.pageLength,
});
}
emitInitialized(): void {
this.initialized.emit({
pageNumber: this.pageIndex.toString(),
pageLength: this.pageLength
});
}
}
paginator html
<div class="paginator">
<mat-form-field class="length-dropdown">
<mat-label>Page Length</mat-label>
<mat-select [(value)]="this.pageLength">
<mat-option *ngFor="let length of pageLengthOptions" [value]="length">{{length}}</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button class="previous-page-navigate" (click)="changePage(-1)" [disabled]="this.pageIndex === 0">
<mat-icon>keyboard_arrow_left</mat-icon>
</button>
<span>{{ this.pageIndex + 1 }} </span>
<button mat-icon-button class="next-page-navigate" (click)="changePage(+1)" [disabled]="viewingLastPage">
<mat-icon>keyboard_arrow_right</mat-icon>
</button>
</div>
parent ts
export class WebsiteConfigComponent implements OnInit {
@ViewChild(WebsiteListTableComponent) table: WebsiteListTableComponent;
@ViewChild(PaginatorComponent) paginator: PaginatorComponent;
ngAfterViewInit(): void {
this.loadWebsites();
}
loadWebsites() {
this.getWebsiteList(this.paginator.pageLength, this.paginator.pageIndex.toString());
}
...
parent html
<div class="website-filters">
<mat-form-field>
<mat-label>Website</mat-label>
<input matInput [(ngModel)]="searchString" autocomplete="off" />
</mat-form-field>
<button mat-raised-button (click)="searchWebsites()">Search</button>
<button mat-raised-button (click)="resetFilters()">Reset</button>
<button mat-raised-button (click)="createNewWebsite()">
Create New Website
</button>
<button mat-raised-button routerLink="/app/configurations">Back</button>
</div>
<div class="websites-table">
<app-website-list-table
[websites]="websites"
(viewWebsite)="editWebsite($event)"
[ngClass]="{'table-loading': this.loading$ | async}"
></app-website-list-table>
</div>
<div class="spinner" *ngIf="this.loading$ | async">
<mat-spinner></mat-spinner>
</div>
<app-paginator #paginator [viewingLastPage]="endOfList" (pageNumberChange)="onPageNumberChange($event)"></app-paginator>
Please paste your parent ts code for using
paginator
part, since there is one thing to consider isAfterViewInit
, you need to wait for the view to be initialized before you can access your@ViewChild
.