I'm trying to make a sequiental requests to ngrx-store to save two different entities in two random mongodb collections and need to have answer from first ngrx-store effect to use data in another one on create entity referring to first one.
What I have:
reducer
export interface UserState {
currentUser: IUser | null;
checkUserResult: any | null;
user: IUser | null;
users: IUser[] | null;
errorMessage: string | null;
isLoading: boolean;
isLoaded: boolean;
isError: boolean;
}
export function userReducer(
state = initialUserState,
action: fromUser.UserActions
): UserState {
switch (action.type) {
case fromUser.USER_CREATE: {
return {
...state,
isLoading: true,
};
}
case fromUser.USER_CREATE_SUCCESS: {
return {
...state,
user: {
_id: action.user._id,
userLogin: action.user.userLogin,
userPassword: action.user.userPassword,
userEmail: action.user.userEmail,
userPhone: action.user.userPhone,
userRole: action.user.userRole,
userType: action.user.userType,
userRef: action.user.userRef,
},
checkUserResult: null,
errorMessage: null,
isLoading: false,
isLoaded: true,
isError: false,
};
}
case fromUser.USER_CREATE_FAILURE: {
return {
...state,
errorMessage: "Can not create user",
isError: true,
};
}
[...]
}
action
export class UserCreate implements Action {
readonly type = USER_CREATE;
constructor(public payload: IUser) {}
}
export class UserCreateSuccess implements Action {
readonly type = USER_CREATE_SUCCESS;
constructor(public user: IUser) {}
}
export class UserCreateFailure implements Action {
readonly type = USER_CREATE_FAILURE;
constructor(public error: string) {}
}
effect
@Effect()
createUser$: Observable<Action> = this.action$.pipe(
ofType(UserActions.USER_CREATE),
map((action: UserActions.UserCreate) => action.payload),
exhaustMap((payload) => {
return this.userDataService.createUser(payload).pipe(
map((data) => {
return new UserActions.UserCreateSuccess(data);
}),
catchError((err) => of(new UserActions.UserCreateFailure(err)))
);
})
);
selector
export const getUserState = createSelector(
fromFeature.getAppState,
(state: fromFeature.AppState) => state.users
);
and service
createUser(user: IUser) {
this.store.dispatch(new fromStore.UserCreate(user));
return this.store.select(fromStore.getUserState);
}
as well as another service (user-data.service.ts) which methods calls from effect
createUser(user: IUser): Observable<IUser> {
const url = `${this.BASE_URL}/user/create`;
return this.http.post<IUser>(url, { ...user });
}
all this setup works while I create only one entity from component
user-create.component.ts
onCreateUser() {
this.us
.createUser({
userLogin: this.createUserForm.value.userLogin,
userPhone: this.createUserForm.value.userPhone,
userEmail: this.createUserForm.value.userEmail,
userPassword: this.createUserForm.value.userPassword,
userRole: this.createUserForm.value.userRole,
userRef: this.createUserForm.value.userRef,
userType: this.createUserForm.value.userType,
})
.subscribe((user) => {
if (user) {
this.createUserForm.reset();
this.router.navigate(["/users"]);
this.us.getUsersList();
}
});
}
Other logic to create different entities are equal to listed above. And when I try to call action inside action within component like this
this.us
.createUser({
userLogin: this.createContactUserForm.value.userPhone,
userPhone: this.createContactUserForm.value.userPhone,
userEmail: this.createContactUserForm.value.userEmail,
userPassword: this.createContactUserForm.value.userPassword,
userRole: this.createContactUserForm.value.userRole,
userRef: this.createContactUserForm.value.userRef,
userType: this.createContactUserForm.value.userType,
})
.subscribe((userState) => {
if (userState.user) {
this.cs
.createContact({
contactFirstName: this.createContactUserForm.value
.contactFirstName,
contactLastName: this.createContactUserForm.value.contactLastName,
contactPatronymicName: this.createContactUserForm.value
.contactPatronymicName,
contactPhone: this.createContactUserForm.value.userPhone,
contactUserId: userState.user._id,
})
.subscribe((contactState) => {
if (contactState) {
this.router.navigate(["/contacts"]);
}
});
}
});
it works as intended, but later, when I try to make any other action - it brokes application state. For example I can't edit or delete contacts after it. On other hand if I split logic to two independent calls - it won't work, because at moment when createContact method calls - application didn't know yet createdUser _id or userState.user._id and throw an error.
I think, here must be a more complex logic, but how make it correct - I have no clue. Please, suggest me proper way to perform this.
Thanks in advance.