Reputation: 537
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:
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
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
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
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