In my app I have multiple forms where the result JSON object may vary in its structure and has nested objects and arrays in different levels. These forms also allow the user to upload files and the object stores and array with url to download, name, etc.
What I do now is turn the file into base64 string, then before any request that has files, I upload them to my backend.
What I want to do is to make that API call of file upload, wait until it finish and once I get response, modify the user's body request, only then make the main post request with these modifications. But is not pausing, the queries are being executed in parallel, I know this because in the backend the file is uploaded but the user's object is not modified, and besides for some reason the query of file upload is being executed several times for no reason.
export class FilesCheckerInterceptor implements HttpInterceptor {
constructor(private filesService: FilesService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const data = request.body;
if (data) {
const uploaded: File[] = [];
this.traverse(data, files => {
files.forEach(file => {
const base64 = file.data;
const result = this.filesService.toFile(base64, file.name);
uploaded.push(result);
});
});
console.log(request);
return this.filesService.uploadFile(uploaded).pipe(
mergeMap(response => {
this.traverse(data, files => {
for (let i = 0; i < response.length; i ) {
files[i] = response[i];
}
});
return next.handle(request.clone({ body: data }));
}),
);
}
else {
return next.handle(request);
}
}
traverse(obj: any, action: (value: InternalFile[]) => void) {
if (obj !== null && typeof obj === 'object') {
Object.entries(obj).forEach(([key, value]) => {
if (key === 'attachments' && Array.isArray(value)) {
// const files = value as InternalFile[];
action(value);
}
else {
this.traverse(value, action);
}
})
}
}
}
CodePudding user response:
I will suggest using forkJoin.
forkJoin will wait for all passed observables to emit and complete and then it will emit an array or an object with the last values from corresponding observables. This operator is best used when you have a group of observables and only care about the final emitted value of each. One common use case for this is if you wish to issue multiple requests on page load (or some other event) and only want to take action when a response has been received for all. In this way, it is similar to how you might use Promise.all.
Or you can use async-await.
Further explanation on forkjoin https://ultimatecourses.com/blog/rxjs-forkjoin-combine-observables
CodePudding user response:
I tried many things but this is the only one that works, I do not know what it is or how it works but by pure try-fail that I achieved the goal.
this goes on the return
statement of the initial code`
const observable = this.filesService.uploadFile(uploaded).pipe(
concatMap(response => {
//replace current representations with server-given representations of files
this.traverse(data, files => {
files.forEach((_, index) => {
files[index] = response[index];
});
});
//make actual request
const clone = request.clone({ body: data });
return next.handle(clone);
}),
);
return observable;