Mark De Verno
Mark De Verno

Reputation: 428

Dispatching sequential events in Effect

My applications requires a two stage approach to uploading file:

  1. Save document metadata in backend; returns url+sas token to upload to Azure Storage
  2. Upload document to azure Storage

It's also important to:

  1. Broadcast Success
  2. Capture Failures

I'm using Angular 9 and NGRX 9, and would like the above to happen as one Action when the user selects a file. From NGRX's documentation, I believe this would be a concatMap operation, but I've yet to have success. The closest I've come is the following:

saveAndUploadDocument$ = createEffect(() => { 
      return this.actions$.pipe(
        ofType(DocumentActions.saveAndUploadDocument),
        switchMap(action => this.documentService.saveDocument(action.file).pipe(
          switchMap(response => this.documentService.uploadDocument(action.file, response.document, response.sasToken).pipe(
              map(token => DocumentActions.saveDocumentSuccess({document: response.document})),
              catchError(error => of(DocumentActions.saveDocumentFailure({ error }))))
            ),
          )
        ),
      );
  });

The above accomplishes the sequential Save + Upload, but fails to broadcast the saveDocumentSuccess action. What should I change?

Related, but perhaps it's another topic... I've read in a few places that it's best practice to limit Effects to one action. I'm not sure how this could be accomplished when you need to chain the result from one action/effect to the next. However, this would make it cleaner if there could be seperate Save and Upload effects. If someone has an idea on how to accomplish this, I'd be interested to know.

Thanks!

Upvotes: 0

Views: 489

Answers (1)

Ravi Teja Vattem
Ravi Teja Vattem

Reputation: 414

IMO, there should be two effects in this scenario

  • The first one should call this.documentService.saveDocument and after success response, it should call another action(to trigger this.documentService.uploadDocument through another effect) instead of chaining it to another switchMap.
  • There should be separate actions for handling uploadDocument.
    saveDocument$ = createEffect(() => {
            return this.actions$.pipe(
                ofType(DocumentActions.saveDocumentAction),
                switchMap((action) =>
                    this.documentService.saveDocument(action.file).pipe(
                        map((res) => {
                            return new DocumentActions.uploadDocumentAction(res);
                        }),
                        catchError((error) =>
                            of(new DocumentActions.saveDocumentFailureAction(error))
                        )
                    )
                )
            );
        });    
        uploadDocument$ = createEffect(() => {
            return this.actions$.pipe(
                ofType(DocumentActions.uploadDocumentAction),
                switchMap((action) => {
                    this.documentService.uploadDocument(action.url).pipe(
                        map((res) => {
                            return new DocumentActions.uploadDocumentSuccessAction(res);
                        }),
                        catchError((error) =>
                            of(new DocumentActions.uploadDocumentFailureAction(error))
                        )
                    );
                })
            );
        });

Upvotes: 1

Related Questions