Search code examples
angularngrxngrx-effects

Angular 13 with ngrx effects returning data from Api


I'm new to ngrx and am trying to return data from an api call in my effect, however I've not constructed my effect correctly and I'm not sure how to correct it for the Effect.

So first off, I have a generic method for calling the Api:

export class CrudService {

//Rest Api
endpoint = 'http://localhost:4200';

constructor(private httpClient: HttpClient) { }

httpHeader = {
  headers: new HttpHeaders({
    'Content-Type': 'aplication/json'
  })
}

getAll<T>(apiMethod: string): Observable<T> {

let callPoint = this.endpoint.concat('/', apiMethod);

return this.httpClient.get<T>(callPoint)
  .pipe(
    retry(1),
    catchError(this.processError)
  )
}.....

My actions file is:

import { Action } from '@ngrx/store';
import { Swivlr } from '../../Models/swivlr.model';

 export const ADD_SWIVLR = '[SWIVLR] Add';
 export const REMOVE_SWIVLR = '[SWIVLR] Remove';
 export const GET_SWIVLR = '[SWIVLR] Get';
 export const FAIL_SWIVLR = '[SWIVLR] Fail';

 export class AddSwivlr implements Action {
   public readonly type = ADD_SWIVLR;

   constructor(public payload: Swivlr) { }
 }

 export class RemoveSwivlr implements Action {
   public readonly type = REMOVE_SWIVLR;

   constructor(public payload: number) { }
 }

 export class GetSwivlr implements Action {
   public readonly type = GET_SWIVLR;

   constructor(public payload: Swivlr[]) { }
 }

 export class FailSwivlr implements Action {
   public readonly type = FAIL_SWIVLR;
   constructor(public error: string | null) { }
 }

 export type Actions = AddSwivlr | RemoveSwivlr | GetSwivlr | FailSwivlr;

so now in my effects file i'm trying to write the effect for getting all records:

import { RemoveSwivlr, AddSwivlr, GetSwivlr, GET_SWIVLR, FAIL_SWIVLR, FailSwivlr } from '../Actions/swivlr.actions';
import { AppState } from '../../app/app.state';
import { CrudService } from '../crud/generic-crud';
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, Observable, of, switchMap } from 'rxjs';
import { Swivlr } from '../../Models/swivlr.model';

@Injectable()
export class SwivlrEffects {
  constructor(private crudService: CrudService,
    private readonly actions$: Actions
  ) { }

  public readonly getSwivlr$: Observable<any> = createEffect(() => {
    return this.actions$.pipe(ofType(GET_SWIVLR),
      switchMap(() => of(this.crudService.getAll<Swivlr[]>('endpointAddress'))),
      map((payload: Swivlr[]) => new GetSwivlr(payload)),
      catchError((error: string | null) =>
        of(new FailSwivlr(error)))
    );
  });
}

The compiler does not like my switchMap line and I expect it's because I've not subscribed to my getAll Method, I'm not sure how I should subscribe in this instance.

my model is declared :

export interface Swivlr {
  key: string;
  name: string;
  url: string;
  width: string;
  height: string;
}

any help On how I should construct my effect correctly would be greatly appreciated.

Thanks to @Deitsch I have changed my effect to:

public readonly getSwivlr$: Observable<any> = createEffect(() => {
    return this.actions$.pipe(ofType(GET_SWIVLR),
      mergeMap(() => this.crudService.getAll<Swivlr[]>('endpointAddress').pipe(
      map((payload: Swivlr[]) => new GetSwivlr(payload)))),
      catchError((error: string | null) =>
        of(new FailSwivlr(error)))
    );
  });

Solution

  • Your syntax for creating the effect is wrong. Check out the movie.effects.ts at ngrx.io/guide/effects and use it as starting point. Replace the API call with yours and go from there.

    getSwivlr$ = createEffect(() => {
        return this.actions$.pipe(ofType(GET_SWIVLR), // use reference, not just same string!!
          mergeMap(() => this.crudService.getAll<Swivlr[]>('endpointAddress').pipe(
          map((payload) => new GetSwivlr(payload)))),
          catchError((error) => of(new FailSwivlr(error)))
        );
    });
    

    Also you don't need to add typing manually everywhere. Typescript handles it properly for you - and if there is an issue it will tell you. Currently it is very cluttered without actually adding anything to it.

    You should also not reference the type GET_SWIVLR in this.actions$.pipe(ofType(GET_SWIVLR) as a string - it is very prone to break without you noticing. Reference the proper actions type!

    On the given link mergeMap is used while you use switchMap - both have their usecases, read more about it blog.angular-university.io/rxjs-higher-order-mapping

    Another thing is you creation of the actions. It works the way you do it but i'd advise you to use the createAction methods like in the documentation ngrx.io/guide/store/actions