2

I'm using Angular and would like to achieve following functionality.

In the form user can upload multiple images. On form submit I'd like to call upload endpoint with post method as many times as there are images (simple for loop). Each time call will return a response with link to google cloud platform storage for each image. Each time I would push the link to an array. Once it looped through all the images I'd like to finally submit post/patch request with the json data from the form and the array with links to google cloud platform.

At the moment I'm struggling with the asynchronous code. The request I would like to call at the very end is being triggered first, hence the image array with link to GCP is empty (event though the images are correctly uploaded to GCP, the upload of the images happen after the document was saved. From similar posts I understood that switchMap might be used but I don't know how to use it in this scenario

#update based on the @Picci comment, works for one image

I had to convert FileList to an array, I did it with

this.filesListArray = Array.from(this.filesList); 

and then implemented Picci solution:

 onSave(){ if(this.form.invalid) { console.log(this.form) return; } const uploadRequests$:Observable<[string]> = this.filesListArray.map(file => from(this.uploadService.upload(file)) // transform Promise to Observable using the rxjs 'from' function .pipe( map(response => response['filePath']), catchError(err => of(`Error while uploading ${file}`)) // if there is an error, return the error ) ); const urlsOrErrors$ = forkJoin(uploadRequests$); urlsOrErrors$.pipe( concatMap(result => this.eventsService.update(this.id, this.form.value, result)) ).subscribe(() => { this.form.reset(); this.router.navigate([this.id]); }) .closed; } 

1 Answer 1

2

If I understand right, you want first upload the images and then save a Json containing, among other things, the list of urls corresponding to the images uploaded.

If this is the case, then I would proceed like this.

First I would transform Promises to Observables and create an array of Observables corresponding to the requests to upload files, something like this

const uploadRequests$ = this.filesList.map(file => from(this.uploadService.upload(file)) // transform Promise to Observable using the rxjs 'from' function .pipe( map(response => response['filePath']), catchError(err => of(`Error while uploading ${file}`)) // if there is an error, return the error ) ) 

Now that you have an array of Observables, you can use the forkJoin function of rxjs to create an Observable that will execute all the upload requests in parallel and emit, as result, an array containing all the responses.

Considering the way we have built the Observables, the responses will be either the url returned by the upload service or an error string.

The code would be something like

const urlsOrErrors$ = forkJoin(uploadRequests$) 

Once urlsOrErrors$ emits, we are ready to execute the last service to save the Json. We need to make sure we execute such service only when urlsOrErrors$ has emitted, and so we use the concatMap operator.

The final complete code would look something like this

const uploadRequests$ = this.filesList.map(file => from(this.uploadService.upload(file)) // transform Promise to Observable using the rxjs 'from' function .pipe( map(response => response['filePath']), catchError(err => of(`Error while uploading ${file}`)) // if there is an error, return the error ) ) const urlsOrErrors$ = forkJoin(uploadRequests$) urlsOrErrors$.pipe( concatMap(urlsOrErrors => this.eventsService.update( this.id, this.form.value, urlsOrErrors )) ) 

If you are interested in common patterns for the use of rxjs with asynchronous services, you may find this article interesting.

Sign up to request clarification or add additional context in comments.

2 Comments

thank you, I have followed your suggestion and managed to fix it for one image. I still don't know how to do that with list of files. In your code this.files.map throws an exception as there is no map feasible on FileList object.
ok my fall, the code works perfectly fine, I had to just convert FileList to an array of File Objects

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.