1
votes

Long story short I'm junior .Net developer and I recived task to change something in our Angular app, I have no idea how it works and I'm struggling to change it. I'm stuck on one topic.

User is created based on the form, after submiting it there is a call:

submit() {
    if (this.accountForm.invalid) {
      return;
    }
    const {
      email,
      accountExpirationDate,
      firstName,
      lastName,
      permissionLevel,
      timezone,
      language,
    } = this.accountForm.value;

    const accountExpirationDateMs = new Date(accountExpirationDate || null).getTime();

    const result = {
      email,
      accountExpirationDate: accountExpirationDateMs || null,
      name: firstName,
      lastName,
      roleExternalIds: [permissionLevel],
      timezone,
      languageExternalId: language,
      avatar: this.avatar
    };

    if (!this.id) {
      this.store.dispatch(fromUser.createUserAction({ result } as any));
    } else {
      this.isPending = true;
      this.store.dispatch(fromUser.editUserAction({ result, id: this.id } as any));
      setTimeout(() => {
        this.store.dispatch(fromSettings.getCurrentUserInfoAction());
      }, 1200);
    }
  }

After the this.store.dispatch(fromUser.createUserAction({ result } as any)); in the if statement I have to make two more calls which requires me to provide userId which is returned by the backend after the user creation, but I have no idea how to extract it from this call.

Create user action looks like this:

export const createUserAction = createAction('[User management] Create User', props<{result: UserRegistrationRequestModel}>());

And it's making this call I belive:

  createUser$ = createEffect(() =>
    this.actions$.pipe(
      ofType(fromUserManagement.createUserAction),
      mergeMap((action) => {
        return this.identityServiceClient.create(action.result).pipe(
          map(response => {
            this.messageService.open('User has been created!', 'success');
            this.router.navigateByUrl('/app/users/list');
            return fromUserManagement.createUserActionSuccess(response)
          }),
          catchError(() => {
            this.messageService.open('Something went wrong!', 'danger');
            return fromUserManagement.createUserActionError;
          })
        );
      })
    )
  )

It might be stupid question with not enough information, but I'm using angular first time in my life so honestly I have no idea how it works :D

2

2 Answers

1
votes

You can dispatch a new action from your createUser$ effect to handle the extra work, and pass the parameters you need within. Then you can create a new effect to handle the new dispatched action and call whatever you want within.

Or if you want to handle it within the same effect after receiving the result from the identityServiceClient.create function, you can try something like the following:

createUser$ = createEffect(() =>
    this.actions$.pipe(
        ofType(fromUserManagement.createUserAction),
        mergeMap((action) => {
            return this.identityServiceClient.create(action.result).pipe(
                map((response) => {
                    this.messageService.open('User has been created!', 'success');
                    this.router.navigateByUrl('/app/users/list');
                }),
                switchMap((response) =>
                    // Call here all the extra requests (request1$, request2$) depending on the response,
                    // and the `forkJoin` will emit the value once all the request have been completed
                    forkJoin({ response1: request1$, response2: request2$ }).pipe(
                        // if you want to do extra work with the results that come from request1$ & request2$ you can do it here using `tap` operator
                        tap(({ response1, response2 }) => console.log(response1, response2)),
                        // and after that you need to return back the action you want to dispatch which is `createUserActionSuccess` in your case
                        map(() => fromUserManagement.createUserActionSuccess(response))
                    )
                ),
                catchError(() => {
                    this.messageService.open('Something went wrong!', 'danger');
                    return fromUserManagement.createUserActionError;
                })
            );
        })
    )
);
0
votes

For some quick background on what you're working with, it appears that your code bases utilizes NgRx for state management. The Actions you see being dispatched are documented here. Similarly, Effects are documented here. The whole interaction lifecycle can be summed up with this image:

https://user-images.githubusercontent.com/3605268/61837742-1851a300-ae54-11e9-97ed-9fb862d0bbf8.png

The above answer is certainly a solution, it just depends what you need to do with the userId after user creation. When Actions are dispatched in NgRx, the expectation is that whatever object did the dispatching has fired that action off, and has no expectation of a return value (the userId in your case) at all.

It looks like if the user already exists, you're simply querying the API for the user's data. So, if this is the case, then I'd create an Effect that listens for successful user creation (fromUserManagement.createUserActionSuccess) and then triggers the same action already mentioned for querying (fromSettings.getCurrentUserInfoAction) :

updateUserAfterCreation$ = createEffect(() =>
    this.actions$.pipe(
        ofType(fromUserManagement.createUserActionSuccess),
        switchMap((action) => 
          fromSettings.getCurrentUserInfoAction()
        )
    )
);

In the above example, I'm assuming that dispatching fromSettings.getCurrentUserInfoAction triggers an Effect that is already in your codebase, and uses any userId already stored in the Store (since getCurrentUserInfoAction does not take any parameters such as userId).

This pattern can be repeated for any specific use case - if you're tasked with updating the user's profile immediately after creation, then instead of pointing the switchMap to fromSettings.getCurrentUserInfoAction you could use fromSettings.updateNewUserAction. Personally, I prefer separating out these Effect chains instead of nesting them in one large pipe in the previous answer - it's easier to test, easier to read, and easier to troubleshoot.