I have an ngrx
action/effect
that takes a FileList
and uploads them sequentially to the server. After the first File
is uploaded, an ID
will be generated that needs to be used by the second File
as argument, so it can be later related to the first one in the back. The way I setup the component-store
is that I have a State
that holds the ID
(initially null
and the files).
The problem arises when I try to update the ID in the store after the first file is uploaded. The concatMap
that I am using to keep the sequential order is still working on the files and its only after all the files are actually uploaded that a new stream comes along the way with the ID that I set after the first call. So, at the end I have more https calls than expected and none of the files are using the ID that was created initially.
The real problem is that I don't know how to get the latest value of the ID from the store at the moment that the concatMap is shooting the files. I am guessing that combineLatest is not in the right place.
Here's what I have tried so far:
readonly uploadFiles = this.effect((files: Observable<FileList>) => {
return combineLatest([
files.pipe(
mergeMap(list => Array.from(list).map(i => ({file: i, length: list.length})) )
),
//selector holding the ID that will be set after the first file is uploaded
this.uploadId$,
]).pipe(
concatMap(([tuple, uploadId], index) =>
this._dataSvc.upload(tuple.file, index, tuple.length, uploadId.ID).pipe(
tap({
next: (event) => {
switch (event.type) {
case HttpEventType.UploadProgress:
this.uploadProgress([
Math.round((event.loaded / event.total) * 100),
index
]);
break;
case HttpEventType.Response:
if(uploadId.ID === null) {
//calls the reducer with the ID of the current set of uploads
this.setUploadId(event.body.ID);
}
}
},
}),
catchError(() => EMPTY) //for now
)
)
)
});
Thanks
Here are my changes after @maxime1992 suggestion:
readonly uploadFiles = this.effect((files$: Observable<FileList>) => {
return files$.pipe(
concatMap((files) =>
of({
ID: null,
}).pipe(
expand((ids, index) =>{
const idx = index/5;
return files.length === idx || index % 5 !== 0 ? EMPTY :
this._dataSvc.upload(files[idx], idx, files.length, ids.ID).pipe(
tap({
next: (event) => {
switch (event.type) {
case HttpEventType.UploadProgress:
this.uploadProgress([
Math.round((event.loaded / event.total) * 100),
idx
]);
break;
case HttpEventType.Response:
this.uploadCompleted(idx);
}
},
}),
map((event, index) =>
event.type === HttpEventType.Response ?
({ ID: event.body.ID }) : ({ ID: null })),
catchError(() => EMPTY) //for now
)
})
)
)
);