I am trying to make POST
request after all files are uploaded. In my web form, the user can add many files to upload. I have two APIs that handle my request.
One API for my fileupload
which saves the file in the database and returns an id (document id), and the second API saves the dossier. Before I call the savedossier
API, I have to add all documents ids to the dossier object.
I upload the files with mergeMap:
const fileupload$ = from(this.attachments).pipe(
mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);
fileupload$.subscribe(
(data) => this.dossier.attachments.push(data),
(err) => console.error('err: ' + err)
);
const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);
savedossier$.subscribe(
(data) => console.log('Dossier saved!'),
(err) => console.error('err: ' + err)
);
My problem is the second call (to savedossier$
) does not wait until all files are uploaded (because it's async). So the document id's are not added to my dossier object.
I had tested with a "ugly" solution like this:
const fileupload$ = from(this.attachments).pipe(
mergeMap(file => this.http.post<Attachement>(`${this.addAttUrl}`, file)),
);
fileupload$.subscribe(
(data) => onuploaded(data),
(err) => console.error('err: ' + err)
);
onuploaded(data) {
this.dossier.attachments.push(data)
if (this.dossier.attachments.length === this.attachments.length) {
savedossier$.subscribe(
(data) => console.log('Dossier saved!'),
(err) => console.error('err: ' + err)
);
}
}
const savedossier$ = this.http.post<Dossier>(`${this.adddossierUrl}`, this.dossier);
This solution with an if statement that compares the array lengths works. But I think it is not a recommended way.
Is there a better solution from rxjs?
If I understand right, you need first to complete all the fileupload operations before saving the document, which has to contain all the
id
s returned by the fileupload operations.I assume also that
this.attachments
is some sort of array containing the names of the attachments.If all this is true, I would proceed like this.
First I would create an array of Observables, each representing the http call you want to make to save an attachment and receive its
id
as responseYou can then execute all these observables in parallel using
forkJoin
like thiswhich returns an Observable which emits when all the observables passed as parameter in the
saveAttachments
array have completed. The value emitted is an array with all theid
s returned.Now you can execute the last operation, i.e. save the dossier, concatenating the
savedossier$
observable to the one returned by the aboveforkJoin
. The final code would look like thisNote that you have to use the
concatMap
operator to specify that you want to execute the observable returned by the function passed as parameter toconcatMap
after the previous observable, i.e. that returned byforkJoin
, has completed.If you want to see some patterns on how to use Observables to compose http calls in different use cases, this article may be of interest.