I have a basic table component. The user is able to reload and filter the data. For those two actions I use Subjects.
private fetchFromServer$ = new Subject<void>();
private filter$ = new Subject<void>();
The data fetch will then be switched into an observable:
private externalData$ = this.fetchFromServer$.pipe(
switchMap(() => this.loadFromServer())
);
Afterwards the observable (with the external data) will be combined with the filter-subject, like this:
data$ = combineLatest([this.externalData$, this.filter$]).pipe(
switchMap(([externalData]) => this.search(externalData))
);
here is the basic view:
<div *ngIf="data$ | async as data">
{{data |json}}
</div>
<button (click)="reload()">reload</button>
<button (click)="filter()">filter</button>
But this implementation has some drawbacks yet:
The table won't be displayed initially. combineLatest needs at least one emit of externalData$ and filter$. If both buttons "reload" and "filter" gets clicked, the table data will be displayed. But that's not the problem, because I want to display the table initially and as a result I want to call
fetchFromServer$.next()
filter$.next();
on the correct position.
But exactly here is my problem. Where to call those two lines? If I put them in the ngOnInit()-method, the data won't be shown. I guess, because the async pipe subscribes to it later.
Or should the Observable be a hot observable with replaying the last emit? But I have no clue how to do that and furthermore it must be unsubscribed in the ngOnDestroy()-method?
Should it be placed into the ngAfterViewInit() lifecycle hook? Then it works, but I get he famous "Expression has changed after it was checked" error.
Here is the sample project: https://stackblitz.com/edit/angular-sl3kku