Escrito el 12/08/2024

NGRX Store - Effects: Effects

Se usa para tener en el store las rutas y manejar todo con el store como debe ser.

State

store/reducers/index.ts

import * as fromRouter from "@ngrx/router-store";
import { ActivatedRouteSnapshot, RouterStateSnapshot, Params } from "@angular/router";
import { ActionReducerMap, createFeatureSelector } from "@ngrx/store";

export interface RouterStateUrl {
  url: string;
  queryParams: Params;
  params: Params;
}

export interface State {
  routerReducer: fromRouter.RouterReducerState<RouterStateUrl>;
}

export const reducers: ActionReducerMap<State> = {
  routerReducer: fromRouter.routerReducer,
};

export const getRouterState = createFeatureSelector<fromRouter.RouterReducerState<RouterStateUrl>>("routerReducer");

export class CustomSerializer implement fromRouter.RouteStateSerialize<RouterStateUrl> {
  serialize(routerState: RouterStateSnapshot): RouterStateUrl {
    const { url } = routeState;
    const { queryParams } = routerState.root;

    let state: ActivatedRouteSnapshot = routerState.root;
    while (state.firstChild) {
      state.firstChild;
    }
    const { params } =  state;

    return {
      url,
      queryParams,
      params
    }
  }
}

// app.module.ts

...
import { reducers, CustomSerializer } from '/store';
import { StoreRouterConnectingModule, RouterStateSerializer } from '@ngrx/router-store';
...
@NgModule({
  import:[
    ...
    Store.forRoots(reducers, { metaReducers }),
    StoreRouterConnectingModule,
    ...
  ],
  ...
  providers: [{ provider: RouterStateSerializer, useClass: CustomerSerializer }],
});
export class AppModule {}

// selectors.ts

...
import * as fromRouter from 'store';
...

export const getSelectProduct = createSelector(
  getProducts,
  fromRoute.getRouterState,
  (entities, router) : Product => router.state && entities[router.state.params.productId]
);

actions.ts

...
import{ Action  } from 'ngrx/store';
import { NavigationExtras } from '@angular/router';

export const GO = '[Router] Go';
export const BACK = '[Router] Back';
export const FORDWARD = '[Router] Forward';

export class GO implements Action {
  readonly type = GO;
  constructor(
    public payload: {
      path: any[];
      query?: object;
      extras?: NavigationExtras
    }
  ){ }
}

export class Back implements Actions {
  readonly type = BACK;
}

export class Forward implements Actions {
  readonly type = FORWARD;
}

export type Actions = Go | Back | Forward;

effects.ts

import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
import { Location } from "@angular/common";

import { Effect, Actions } from "@ngrx/effects";

import * as RouterAtions from "./action/touter.action";
...

@Injectable()
export class RouterEffects {

  constructor (
    private actions$ Actions,
    private router: Router,
    private location: Location
  ){}

  @Effect({ dispatch:false })
  navigate$ = this.actions$
    .ofType(RouterActions.GO)
    .pipe(
      map((action: RouterActions.Go) => action.payload),
      tap(({ path, query: queryParams, extras }) => {
        this.router.navigate(path, { queryParams, ...extras});
      })
    );

  @Effect({ dispatch:false })
  navigateBack$ = this.actions$
    .ofType(RouterActions.BACK)
    .pipe(tap(() => this.location.back()));

  @Effect({ dispatch:false })
  navigateBack$ = this.actions$
    .ofType(RouterActions.FORWARD)
    .pipe(tap(() => this.location.forward()));
}