1

I am trying to execute multiple API calls in ngrx effect using switchmap

The action takes input array of files to be uploaded and api supports one file at a time

My effect look like below

uploadFiles$ = createEffect(() =>
        this.actions$.pipe(
            ofType(uploadFiles),
            switchMap(({id, files}) => {

                const uploadApiList = []
                for(let i =0 ; I<files ; i++) {
                    uploadApiList.push(this.apiService.uploadFile(id, files[i]));
                }
                 return uploadApiList;
    
            }), 
            tap(result=> console.log(result ),
            tap(res=> {return uploadFilesSuccess({response: res})}),
            catchError(error => of(uploadFailure({ errorResponse: error })))
        ),
            { dispatch: false }
    );

The service method

public uploadFile(id: string, file: File): Observable<any> {
        console.log('***************')
        const formData: FormData = new FormData();
        formData.append('file', file);

        return new Observable(observer => {
        
            this.store.dispatch(showLoadingMask());
            this.httpClient.post<any>(`${this.baseUrl}/${id}/upload-files`, formData).subscribe(
                (response: HttpResponse<any>) => {
                    console.log(formData)
                    observer.next(response?.body);
                    observer.complete();
                    this.store.dispatch(hideLoadingMask());
                },
                error => {
                    this.logService.error('[UNEXPECTED_RESPONSE]', error);
                    observer.error(error);
                    observer.complete();
                    this.store.dispatch(hideLoadingMask());
                }
            );
        });
    }

however the api never get called and result printed as array of Observables, also I can see method I invoked the but http call is not happening

I can't figure out What I am doing wrong ?

2 Answers 2

1

I don't have much experience with ngrx but we need to do a forkJoin for an array of observables (uploadApiList) to get the inner values, also there is not need to for the subscribe inside the uploadFile, we can just use map and catchError!

Also tap does not accept a return value (uploadFilesSuccess({response: res})) so I changed it to a map which accepts a return value!

uploadFiles$ = createEffect(() =>
    this.actions$.pipe(
        ofType(uploadFiles),
        switchMap(({id, files}) => {
            this.store.dispatch(showLoadingMask());
            const uploadApiList = [];
            files.forEach((file: any) => {
                uploadApiList.push(this.apiService.uploadFile(id, file));
            });
            return forkJoin(uploadApiList);
        }), 
        tap(result=> {    
            this.store.dispatch(hideLoadingMask());
            console.log(result);
        }),
        map(res => {
            return uploadFilesSuccess({response: res});
        }),
        catchError(error => { 
            this.store.dispatch(hideLoadingMask());
            return of(uploadFailure({ errorResponse: error }))
        }),
    ),
        { dispatch: false }
);

Service:

public uploadFile(id: string, file: File): Observable<any> {
    console.log('***************')
    const formData: FormData = new FormData();
    formData.append('file', file);

    return this.httpClient.post<any>(`${this.baseUrl}/${id}/upload-files`, formData).pipe(
        map((response: HttpResponse<any>) => {
            return response?.body;
        },
        catchError(error => {
            this.logService.error('[UNEXPECTED_RESPONSE]', error);
            throw error;
        }),
    );
}
0

I think, your code does not run as expected because on the one hand your for-loop is wrong:

...            
switchMap(({id, files}) => {

  const uploadApiList = []
  for(let i =0 ; I<files ; i++) {
    uploadApiList.push(this.apiService.uploadFile(id, files[i]));
  }
return uploadApiList;
    
}),
...

The condition should look like this (i < files.length):

let i = 0 ; i < files.length ; i++

On the other hand a switchMap has to return an Observable. You return an array uploadApiList.

Therefore, you have to correct your for-loop and correct your switchMap.

You could simplify this switchMap-part to this:

...            
switchMap(({id, files}) =>
  forkJoin(files.map(file => this.apiService.uploadFile(id, file)))
),
...

forkJoin takes an array of Observables and waits until all are completed (your upload), then returns the result of each Observable in a new array-Observable.


As a big recommendation: Use Typescript and type your code strictly. All these errors would have been found by your IDE ;-)

Not the answer you're looking for? Browse other questions tagged or ask your own question.