Strange oddities to data access in a Angular 8 component
There are two problems here:
- You are mixing imperative and reactive programming.
You can in no way know when the forkJoin will emit. Therefore, you cannot be sure that the instance variable bibliographicCitations
will have updated when you return from getBibliographicCitations
. In could be synchronous or asynchronous. You need to make the method observable:
getBibliographicCitations(): Observable<Array<BibliographicCitation>>;
A simple way of doing this would be to refactor the method to just setup an Observable
:
private refreshSub = new Subject<void>();
private bibliographicCitations$: Observable<BibliographicCitation[]>;
refresh(): void {
this.refreshSub.next();
}
private buildObservables(): void {
this.bibliographicCitations$ = this.refreshSub.pipe(
switchMap(() => forkJoin(this.getHttpCallsOBSStream()),
map(responses => {
// Get all elements from response.
const elements = responses.reduce((acc, response) => [
...acc,
...parser.parseFromString(response, 'text/xml').getElementsByTagName('bibl')
], [] as Element[]);
// Use all elements to query for stuff.
return elements.reduce((acc, element) => {
if (['author', 'title', 'date'].every(tag => element.getElementsByTagName(tag).length === 0)) {
return [...acc, { title: element.textContent.replace(/\s+/g, ' ') }];
} else {
return [...acc, {
authors: element.getElementsByTagName('author'),
title: `${element.getElementsByTagName('title')[0]}`.replace(/\s+/g, ' '),
date: element.getElementsByTagName('date')[0],
}];
}
}, [] as BibliographicCitation[]);
})
shareReplay(1)
);
}
Then you can add a getter-method for that Observable
in your service.
getBibliographicCitations(): Observable<Array<BibliographicCitation>> {
return this.bibliographicCitations$;
}
And the refresh-method can be used to retrigger a read.
With all that in place, you can use the getBibliographicCitations
inside the component and subscribe to it there. The key is that you should only subscribe when you're truly ready to use the value. Storing emissions from an observable is an anti-pattern.
- You are creating new subscriptions at every call to
getBibliographicCitations
Every time you call your method getBibliographicCitations
a new Subscription
is created. That means that after calling it three times, there will be 3 subscriptions operating with their own DOMParser
. And each one will modify the instance variable bibliographicCitations
.
If you want to avoid duplicate subscriptions, you would have to unsubscribe on previous subscriptions before creating new ones. But, none of that would be necessary if you go with the code above and set up the Observable
once.