Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular ngrx - Show Loading gif

I have a side effect like this:

@Effect()
FetchAllOrders$ = this.actions$
    .ofType(SalesOrderActions.FETCH_ALL_ORDERS)
    .switchMap((action: Action) => {
        return this.soApiService.getUsersSalesOrders(action.payload);
    })
    .map((salesOrders: ListDto<SalesOrderList>) => this.actions.fetchAllOrdersSuccess(salesOrders));

I would like to show a loading symbol at the start of the effect and hide it at the end.

I've created a separate set of Actions, Reducers and store state to handle showing the loading symbol.

export class BusyActions {
static SHOW_SPINNER = "SHOW_SPINNER";
static HIDE_SPINNER = "HIDE_SPINNER";

showSpinner(): Action {
   return { type: BusyActions.SHOW_SPINNER };
}

hideSpinner(): Action {
   return { type: BusyActions.HIDE_SPINNER };
}

export const BusyState: IBusyState = {
   visible: false,
   busy: false
};

I've done it this way because the loading state needs to be shared with other modules, states, etc.

How do i call my BusyActions from the side effect? I would need to call the SHOW_SPINNER at the start and HIDE_SPINNER at the end.

Have i done this correctly? Or is there a better way to handle this?

like image 782
user2859298 Avatar asked Dec 11 '17 14:12

user2859298


2 Answers

Let's take an example: If you want to fetch a user.

What I usually do for that:
- Dispatch an action FetchUser
- In the reducer, set a flag on users: isUserFetching: true
- Catch that action within an effect
- Call what you want, a service for example
- When the service returns what he has fetched, map that to a success action: FetchUserSuccess
- If something went wrong, map the error to an error action: FetchUserError
- In your user reducer, if for FetchUserSuccess do what you want + set isUserFetching: false
- In your user reducer, if for FetchUserError do what you want + set isUserFetching: false

@Effect()
fetchUser$: Observable<Action> = this.actions$
  .ofType(FetchUser)
  .pipe(
    switchMap(action =>
      this.usersService.fetchUser(action.payload).pipe(
        map(
          res =>
            new FetchUserSuccess({
              id: action.payload.id,
              data: res,
            })
        ),
        catchError((err: HttpErrorResponse) => 
          of(new FetchUserError(action.payload))
        )
      )
    )
  );

Within your view you can select the good part of the store into a variable, let say isFetchingUser$ and do:

<div *ngIf="isFetchingUser$ | async; else #loadingUser">
  Show the user here
</div>

<div loadingUser>
  Fetching user information
</div>
like image 100
maxime1992 Avatar answered Oct 12 '22 10:10

maxime1992


To achieve this you can use this npm package https://www.npmjs.com/package/angular2-busy

After adding all the necessary module to use the package in your case you need to declare a variable

busy: Promise<any>; 

or

busy: Subscription;

And then use this variable like below

this.busy = this.actions$
.ofType(SalesOrderActions.FETCH_ALL_ORDERS)
.switchMap((action: Action) => {
    return this.soApiService.getUsersSalesOrders(action.payload);
})
.map((salesOrders: ListDto<SalesOrderList>) => this.actions.fetchAllOrdersSuccess(salesOrders));

If you want to use your busyActions class then you have to import this class into your component and create a private variable in the component constructor and call your desired action of the class where it is necessary. But to me it is better to use that above package which works very good.

like image 33
Manzurul Avatar answered Oct 12 '22 09:10

Manzurul