NgRX Effect for downloading media file and dispatching progress Actions until Finish action is dispatched
NgRX Effect for downloading media file and dispatching progress Actions until Finish action is dispatched
I am learning NGRX and RXJS in my Podcast project.
My current question is: how to call progress Actions within my @effect based on my Download Action.
The effect looks like this and works:
@Effect()
downloadEpisodes$ = this.actions$.ofType( episodeActions.DOWNLOAD_EPISODE )
.pipe(
map( ( action: episodeActions.DownloadEpisodes ) => action.payload ),
switchMap( ( episode ) =>
return this.episodesService
.download( episode ) // -> download the media file as blobl
.pipe(
map( event => this.episodesService.getHttpProgress( event ) ), // => report download progress
tap( message =>
console.log( message );
),
// switchMap( response => new episodeActions.DownloadProgressEpisodes( response ) // => doesn't work cause its not iterable accoring to typescript.
last(), // => emit last (completed) message to caller
switchMap( response =>
return this.episodesService.downloadSuccess( response ) // => process response blob
.pipe(
tap( response => console.log('--->', response )),
map( response => new episodeActions.DownloadEpisodesSuccess( response ) ),
catchError( error => of (new episodeActions.DownloadEpisodesFail( error ) ) )
)
)
)
)
);
This will report the following in my console:
Downloading file
episodes.effect.ts:57 File is 7% downloaded.
episodes.effect.ts:57 File is 11% downloaded.
episodes.effect.ts:57 File is 15% downloaded.
episodes.effect.ts:57 File is 18% downloaded.
episodes.effect.ts:57 File is 26% downloaded.
episodes.effect.ts:57 File is 30% downloaded.
episodes.effect.ts:57 File is 44% downloaded.
episodes.effect.ts:57 File is 48% downloaded.
episodes.effect.ts:57 File is 70% downloaded.
episodes.effect.ts:57 File is 74% downloaded.
episodes.effect.ts:57 File is 100% downloaded.
episodes.effect.ts:57 HttpResponse headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:3000/episodes/https%3A%2F%2Fwww.s…ple-videos.com%2Faudio%2Fmp3%2Fcrowd-cheering.mp3", ok: true, …
What do I want to change?
Well, the tap message
under the getHttpProgress
function is called as many times until the upload is done. Then the last()
call forwards the httpResponse
to the next switchMap
.
tap message
getHttpProgress
last()
httpResponse
switchMap
What I would like to achieve is to dispatch an action new episodeActions.DownloadProgressEpisodes( message )
during the Download progress (multiple times) to store the download progress within the episode (call the reducer to update the episode entity).
new episodeActions.DownloadProgressEpisodes( message )
Unfortunately, I don't understand how to dispatch multiple DownloadProgressEpisodes
actions during the download
process without breaking or the last switchMap
that calls the this.episodesService.downloadSuccess( response )
.
I tried to add a action call after the tap
, before the last
call as it seemed to be an Iterator
. However, it didn't dispatch those actions, nothing showed up in my redux devTool.
When I dispatch the DownloadProgressEpisode
from my service within the getHttpProgress
call, It works but I am not sure if that is the proper way.
DownloadProgressEpisodes
download
switchMap
this.episodesService.downloadSuccess( response )
tap
last
Iterator
DownloadProgressEpisode
getHttpProgress
In the end I want to see the following in my redux devTool:
[Podcast] Download Episode;
[Podcast] Download Episode Progress; // payload 0%
[Podcast] Download Episode Progress; // payload 10%
...etc
[Podcast] Download Episode Progress; // payload 100%
[Podcast] Download Episode Success;
What is the best way to solve this?
Or alternatively, should I use different effects for the Download, which calls an effect for the DownloadProgress which repeats itself until finished and then call and effect for DownloadSuccess?
1 Answer
1
I would split this effect in several smaller ones. Right now you're doing several things in the same effect: download, update progress and process the result. You can try to create an effect for each of them.
Download the episode:
@Effect()
downloadEpisode$ = this.actions$.pipe(
ofType(episodeActions.DOWNLOAD_EPISODE),
switchMap(( payload ) => this.episodesService
.download(payload).pipe(
map(response => new episodeActions.DownloadEpisodesSuccess(response)),
catchError(err => of(new episodeActions.DownloadEpisodesFail(error))),
)
)
)
Catch the download success action and process it:
@Effect()
processEpisode$ = this.actions$.pipe(
ofType(episodeActions.DOWNLOAD_EPISODE_SUCESS),
switchMap(( payload ) => this.episodesService
.downloadSuccess(payload).pipe(
map(response => new episodeActions.ProcessEpisodesSuccess(response)),
catchError(err => of(new episodeActions.ProcessEpisodesFail(error))),
)
)
)
And about showing/updating the progress:
@Effect()
updateProgress$ = this.actions$.pipe(
ofType(episodeActions.DOWNLOAD_EPISODE),
switchMap(( payload ) => this.episodesService
.getHttpProgress(payload).pipe(
map(res => new episodeActions.DownloadEpisodesProgress(res)),
)
)
)
This way of showing the progress assumes that getHttpProgress method will return a new observable each time a new episode is downloaded. We listed for the action that notifies the download was started and then we switch map to the output of getHttpProgress.
Is there a preference for
downloadEpisodes$ = this.actions$.pipe( ofType( episodeActions.DOWNLOAD_EPISODE ),
or this.actions$.ofType( episodeActions.DOWNLOAD_EPISODE ).pipe
or is it all the same? I know actions$
offers an ofType
operator out of the box.– Mattijs
Aug 26 at 7:12
downloadEpisodes$ = this.actions$.pipe( ofType( episodeActions.DOWNLOAD_EPISODE ),
this.actions$.ofType( episodeActions.DOWNLOAD_EPISODE ).pipe
actions$
ofType
also, the response of the
this.episodesService.download
is of the type Observable<HttpEvent<any>>
. Only when HttpEvenType is 4, will there be a response that can be send to ProcessEpisodeSuccess.– Mattijs
Aug 26 at 7:41
this.episodesService.download
Observable<HttpEvent<any>>
So I think in the first DOWNLOAD_EPISODE effect I have to test the response (HttpEvent) for its type and based on that dispatch a PROGRESS or SUCCESS action. I will have a play.
– Mattijs
Aug 26 at 7:46
There is an ofType method on actions but as far as I know it will be removed in favor of the operator ofType to be used inside the pipe method.
– Adrian Fâciu
Aug 26 at 9:30
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I did occur to me to do that but I was wondering if that would create to many effects. I read some opinions about relying to much on Effects and how it becomes to abstract. But in this case, there are 3 separate actions with reducers, so it make sense to use separate Effects for them, yes.
– Mattijs
Aug 26 at 3:14