How to replace facade code in ngrx effects Angular?

275 Views Asked by At

I am using facade pattern with ngrx. I am calling facade methods inside the ngrx effects but I have read that it is not a good practice. I would like to know how can I resolve it.

My code is given below:

**My Effects code : **

closeCountSuccess$ = createEffect(() =>
    this.actions$.pipe(
      ofType(CountActions.CountActionTypes.CLOSE_COUNT_SUCCESS),
      switchMap((action: fromActions.CountSuccess) => {
        this.facade.resetCountList();
        this.facade.setPageNumber({ pageNumber: 0 });
        this.facade.fetchCountList({ page: 0, size: 20 });
        this.facade.setCount();
        this.openSimpleSnackbar(
          this.translateService.instant('content.count-is-successfully-closed'),
          5000,
          G3SnackType.Success
        );
        return of(new fromActions.WizardCloseDialog());
      })
    )
  );

My Facade code:

import { Injectable } from '@angular/core';

import { Observable } from 'rxjs';

import { Store } from '@ngrx/store';

import * as detailsActions from '../../count-details/store/count-details.actions';
import * as actions from '../../count-list/store/count-list.actions';
import { CountDef, CurrentBalanceResponseDef } from '../../shared/interfaces/current-balance-def.interface';
import * as fromActions from './count.actions';
import * as fromSelectors from './count.selectors';
import { CountState } from './count.state';

@Injectable({ providedIn: 'root' })
export class CountFacade {
  constructor(private store$: Store<CountState>) {}

  openDialog(): void {
    this.store$.dispatch(new fromActions.WizardOpenDialog());
  }

  closeDialog(): void {
    this.store$.dispatch(new fromActions.WizardCloseDialog());
  }

  getDialogOpened$(): Observable<boolean> {
    return this.store$.select(fromSelectors.isOpened);
  }



  fetchCountList({ page, size }) {
    this.store$.dispatch(new actions.GetCountListRequest({ page: page, size: size }));
  }

  resetCountList() {
    this.store$.dispatch(new actions.Reset());
  }

  setPageNumber({ pageNumber }): void {
    this.store$.dispatch(new actions.SetPageNumber({ pageNumber: pageNumber }));
  }

  setCurrentCount(): void {
    this.store$.dispatch(new detailsActions.SetCurrentCountRequest());
  }
}

If you know how it can be done in better way then please let me know.

2

There are 2 best solutions below

1
H S W On BEST ANSWER

It can be done as follow:

import * as CountListActions from '../../count-list/store/count-list.actions';
import * as CountDetailsActions from '../../count-details/store/count-details.actions';

closeCountSuccess$ = createEffect(() =>
    this.actions$.pipe(
      ofType(CountActions.CountActionTypes.CLOSE_COUNT_SUCCESS),
      switchMap((_: fromActions.CloseCountSuccess) => {
        this.openSimpleSnackbar(
          this.translateService.instant('content.count-is-successfully-closed'),
          5000,
          G3SnackType.Success
        );
        return of(
          new CountActions.WizardCloseDialog(),
          new CountListActions.SetPageNumber({ pageNumber: 0 }),
          new CountListActions.GetCashClosingListRequest({ page: '0', size: '20' }),
          new CountDetailsActions.SetCurrentCountRequest()
        );
      })
    )
  );
0
hmartini On

I advise you strongly to take a look at the documentation. On the linked page you will find a diagram that describes very well the responsibilities of the individual elements in the NGRX framework. Diagram

As you can see, there is no connection between effects and the UI Layer (snackbar, translation, ...). Don't use effects like an event manager.

With the following (pseudo)-code, I want to give you an idea, how to deal with the pages state inside components and where to use UI elements:

count.reducer.ts

const CountState = {
  listCount: number;
  pageNumber: number;
}

on(resetCount, (state) => ({
  ...state,
  listCount: 0,
  pageNumber: 0,
}))

count.actions.ts

const resetCount = createAction('[Count] reset');

count.selectors.ts

const selectState = createFeatureSelector(COUNT_STATE_FEATURE_KEY);

export const selectListCount = createSelector(
  selectState,
  ({ listCount }) => listCount,
);

export const selectPageNumber = createSelector(
  selectState,
  ({ pageNumber }) => pageNumber,
);

count.facade.ts

export class CountFacade {
  listCount$ = this.store.select(selectListCount);
  pageNumber$ = this.store.select(selectPageNumber);

  constructor(private readonly store: Store);

  resetCount() {
    this.store.dispatch(resetCount());
  }
}

count.component.ts

export class CountComponent {
  listCount$ = this.facade.listCount$.pipe(
    map(listCount) => if (listCount === 0) { 
      this.snackbar.open(
        this.translate.instant('...'),
        5000,
        G3SnackType.Success
      ));
    }
  );

  constructor(
    facade: CountFacade,
    snackbar: SnackbarService,
    translate: TranslationService, 
  ) {}
}

count.component.html

<ng-container *ngIf={ 
  listCount: facade.listCount$ | async,
  pageNumber: facade.pageNumber$ | async
} as vm>
  <p>{{ vm.pageNumber }}</p>
  <button (click)="facade.resetCount()">Reset</button>
</ng-container>