I want to display an array of data fetched by a service in a table component after the service is triggered by a button elsewhere. I've tried to do it using ngOnChanges() but that doesn't appear to notice any changes to the array in the service class after init. I want the flow to be something like this:
PixSearchComponent button click (code not shown) --> PixSearchService data fetch triggered (got this part) --> updated array displayed in PixTableComponent
I did some logging/debugging and the service method is definitely being called. I know it's not something wrong with the table's field binding because I've tested that. Can anyone tell me how to in a sense push the updated array from the service to the table component so that the changes will be reflected in the table? Thanks.
pix-search.service.ts
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http';
import { EventEmitter, Inject, Injectable, Optional } from '@angular/core';
import { catchError, map, tap, throwError } from 'rxjs';
import { IPix } from './model/IPix';
@Injectable({
providedIn: 'root',
})
export class PixSearchService {
constructor(private http: HttpClient) {}
pixUpdated: EventEmitter<IPix[]> = new EventEmitter();
setPixData(pixData: IPix[]) {
this.pixData = pixData;
return this.pixUpdated.emit(this.pixData);
}
getPixData() {
return this.pixData;
}
pixData!: IPix[];
pixUrl: string = 'https://example.ckp-dev.example.com/example';
retrievePixData(): void {
const headers = new HttpHeaders({
'x-api-key':
'ewogICAgImFwaUtleSIgOiAiMTIzIiwKICAgICJ1c2VySWQiID3649807253098ESSBEZXZlbG9wZXIiCn0=',
});
this.setPixData(this.http
.get<any>(this.pixUrl, {
headers
})
.pipe(
tap((data) => console.log('All:', JSON.stringify(data))),
map((data: any) => data.results),
catchError(this.handleError)
) as unknown as IPix[]);
}
handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code:: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(() => errorMessage);
}
}
pix-table.component.ts
import {
Component,
Inject,
Input,
OnChanges,
OnDestroy,
OnInit,
Optional,
} from '@angular/core';
import type { TableSize } from '@dauntless/ui-kds-angular/table';
import type { TableStickyType } from '@dauntless/ui-kds-angular/table';
import type { TableScrollType } from '@dauntless/ui-kds-angular/table';
import { CardElevation } from '@dauntless/ui-kds-angular/types';
import { PixSearchService } from '../pix-search.service';
import { Observable, Subscription } from 'rxjs';
import { IPix } from '../model/IPix';
import { IContract } from '../model/IContract';
import { IAudit } from '../model/IAudit';
import { ICapitation } from '../model/ICapitation';
import { IChangeRequest } from '../model/IChangeRequest';
import { IHnetAudit } from '../model/IHnetAudit';
import { IProduct } from '../model/IProduct';
import { IProvider } from '../model/IProvider';
@Component({
selector: 'pix-table-component',
templateUrl: 'pix-table.component.html',
styleUrls: ['pix-table.component.css'],
providers: [PixSearchService]
})
export class PixTableComponent implements IPix {
constructor(private pixSearchService: PixSearchService) {
this.pixSearchService.pixUpdated.subscribe((pix) => {
this.pixRecords = this.pixSearchService.getPixData() as unknown as IPix[];
});
}
columns = [
'ID',
'Network',
'LOB',
'HP Code',
'Atypical',
'TIN',
'GNPI',
'Org',
'Business Unit Code',
'National Contract',
'National ContractType',
'Contract Type',
'Super Group',
'Contract ID',
'Amendment ID',
'Contract Effective Date',
'Contract Termination Date',
];
rows: any;
tableSize: TableSize = 'small';
showHover = true;
sticky: TableStickyType = 'horizontal';
scrollType: TableScrollType = 'both';
label = 'Payment Index Management';
disabled = 'disabled';
error = 'error';
maxlength = 'maxlength';
showCounter = false;
elevation: CardElevation = 'medium';
legacyConfigTrackerId!: number;
contract!: IContract;
audit!: IAudit;
capitation!: ICapitation;
changeRequest!: IChangeRequest;
claimType!: string;
deleted!: string;
hnetAudit!: IHnetAudit;
id!: string;
noPayClassReason!: string;
payClass!: string;
product!: IProduct;
provider!: IProvider;
rateEscalator!: string;
status!: string;
selected: boolean = false;
pixRecords: IPix[] = [];
errorMessage: string = '';
}
I ended up using a Subject() and subscription to alert the table component to the change in the service class' IPix array:
pix-search.component.ts
constructor(private _pixSearchService: PixSearchService) {}
//trigger update of the IPix array in the service class
buttonClicked(button: any) {
this._pixSearchService.getPixData();
}
pix-table.component.ts
pixRecords$!: Observable<IPix[]>;
constructor(private _pixSearchService: PixSearchService) {
//subscribe to the service class' Subject()
this._pixSearchService.invokeEvent.subscribe((pixData) => {
console.log(pixData);
this.pixRecords$ = pixData;
console.log(JSON.stringify(pixData));
});
}
pix-search.service.ts
public invokeEvent: Subject<any> = new Subject();
pixData!: Observable<IPix[]>;
getPixData(): void {
const headers = new HttpHeaders({
'x-api-key':
'edfg45mFwaUtle345345yICAgICJ1c2VySWQiIDogIlBESSBEZfes39wZXIiCn0=',
});
//assign GET result to pixData array
this.pixData = this.http
.get<any>(this.pixUrl, {
headers
})
.pipe(
tap((data) => console.log(JSON.stringify(data))),
map((data: any) => data.results),
catchError(this.handleError)
);
//alert subscriber (pix-table.component.ts) of the change to pixData
this.invokeEvent.next(this.pixData);
}
Then in the html I used '| async' to allow iteration over the Observable
pix-table.component.html
*ngFor="let row of pixRecords$ | async"