Within my Angular component, I am making an API call by passing a hash string extracted from the current query parameters. Upon receiving the API result, a new hash is also obtained and set as the new hash query parameter. Subsequently, the next API call will be made with this new hash value. A simplified (mock) version of the code snippet is presented below:
import { Component} from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Observable, filter, first, mergeMap, of } from 'rxjs';
export class ResourceTableComponent{
constructor(
private readonly route: ActivatedRoute,
private readonly router: Router
) {}
protected hash = firstValueFrom(
this.route.queryParams.pipe(
filter((params) => 'hash' in params),
mergeMap((params) => this.fetch(params['hash'])),
mergeMap((hash) =>
this.setQueryParams({ hash }).then(() => hash)
)
)
);
protected fetch(hash: string): Observable<string> {
console.log('fetch', hash);
return of(Date.now().toString());
}
private setQueryParams(queryParams: Params): Promise<boolean> {
return this.router.navigate([], {
queryParams,
queryParamsHandling: 'merge',
});
}
}
The issue arises where the fetch
function is called four times as evidenced by the following logged output:
fetch 1691227799752
fetch 1691228130070
fetch 1691228130110
fetch 1691228130131
The desired behavior is for fetch
to be invoked only once, update the hash query parameter with the result, and trigger subsequent calls to fetch
only upon page reload or external call.
To achieve this, using first()
is one possible solution:
this.route.queryParams.pipe(
filter((params) => 'hash' in params),
first(), // <- rxjs first()
mergeMap((params) => this.fetch(params['hash'])),
mergeMap((hash) =>
this.setQueryParams({ hash }).then(() => hash)
)
)
However, it does raise the question of why such intervention is necessary. While acknowledging that queryParams
behaves as a BehaviorSubject
, considerations have been made regarding its emission cycle to comprehend the reason for the observed scenario involving multiple calls.