Evan
Evan

Reputation: 537

ngrx effect dispatch action error and null when passing action payload

I have this effect that dispatches a dummy action if the final post succeeds.

@Effect() post$: Observable<Action> = this.actions$
        .ofType(PropertyActions.UPLOAD_FILE_GETSIGNEDURL)
        .switchMap((action: PropertyActions.UploadFileGetsignedUrl) => {
            this.actionData = action.payload;
            return this.authService.getAuthenticatedUser().getSession((err, session) => {
                if (err) {
                return;
                }

                // post to API gateway
                return this.httpClient.post('https://abcd.execute-api.us-east-1.amazonaws.com/dev/', {
                    title: 'foo',
                    body: 'bar',
                    userId: 1
                    })
                    .pipe(map(res => {
                            console.log("res from signed url: " + res);
                            this.httpClient.post(res.toString(), this.actionData)
                            .pipe(map(res => {
                                console.log("res from upload: " + res);
                                return new PropertyActions.OpenAllProperties(res);
                            }))
                            //return new PropertyActions.OpenAllProperties(res);
                        },
                        err => {
                            console.log("Error occured");
                            return new PropertyActions.OpenAllProperties(null);
                        })
                    );
                }
            )
        }
    )

But 2 things that are wrong:

  1. I get this error: ERROR Error: Effect "PropertyEffects.post$" dispatched an invalid action: undefined core.js:1427 ERROR TypeError: Actions must be objects This error is generated in the console after step past the inner most post: this.httpClient.post(res.toString(), this.actionData). Also note the inner console.log never gets hit

  2. I am trying to pass the action.payload of the first callback into the inner most post but get null. this.actionData is a variable of the Component I set using:

@Injectable()
export class PropertyEffects {

    private actionData: string;

    constructor(
        private actions$: Actions,
        private httpClient: HttpClient,
        private store: Store<fromApp.AppState>,
        private authService: AuthService
    ){}

    @Effect() post$: Observable<Action> = this.actions$
    ...

How to pass the action.payload through to the inner most post? Thank you in advance for your help! I am new to Angular and Rxjs so appreciate your time.

Upvotes: 1

Views: 7807

Answers (2)

Kalakar Sahoo
Kalakar Sahoo

Reputation: 31

In the original question,

the innermost post: this.httpClient.post(res.toString(), this.actionData) might be returning either undefined or empty. Angular Effect expects you to handle such situations by returning an action. You can do so as following:

return new ErrorAction({msg: "Data could not be loaded"})

In your ErrorAction:

export class ErrorAction implements Action {
    readonly type = ActionTypes.ErrorAction;
    msg: String;
    constructor(error: any) {
        this.msg = error.msg;
    }
}

And, finally subscribe to the ErrorAction type and display the error message. You can make use of NotificationsService as follows:

this.actions.pipe(ofType(ActionType.ErrorAction)).subscribe((error: any) => {
    this.notificationService.error(error.msg);
});

Upvotes: 0

ramon22
ramon22

Reputation: 3618

You should not clutter your effects in this way bad for testing and reuse, try to remove the HTTP call to a service and inject then use it in your effect. lets say you placed it in MyHttpService like so

@Injectable()
export class MyHttpService {
  constructor(private http: HttpClient) {
  }        

  addItem(payload: AddItemPayload): Observable<AddItemSuccessPayload> {  
    return this.http.post<AddItemSuccessPayload>('/api/items/' + payload.id, payload.Data).pipe(
      map((data) => {
        return {item: data.item};
      })
    );
  }
}

now we inject it in the effect and pass action.payload through to the inner the service if error or success we dispatch it to other effects

 @Injectable()
 export class VendorEffect {

  @Effect() 
  addItem$ = this.actions$.pipe(
    ofType(Vendor.ActionType.ADD_ITEM),
    map((action: Vendor.AddItem) => action.payload),
    exhaustMap((request) =>
      this.myHttpService.addItem(request).pipe(
        map(payload => new Vendor.AddItemSuccess(payload)),
        catchError(err => of(new Vendor.AddItemFail(err)))
      )
    )
  );


      constructor(private actions$: Actions, private myHttpService: MyHttpService) {
      }

    }

Thats abut it and this is the method used now with ngrx using pipes with ofType

Upvotes: 4

Related Questions