1
votes

I'm trying to use NgRx/effects for my material autocompleted in angular 8.

I have created store, action, effects and reducers but I'm not getting the state after successfully calling the api. The api is returning the correct value.

Action file

import { Action } from '@ngrx/store';
import {IProviderSearchObject} from '../../models/provider.type';

export enum ProviderSearchActionTypes {
  SearchProvidersRequest = '[SEARCH_PROVIDER] REQUEST',
  SearchProvidersSuccess = '[SEARCH_PROVIDER] SUCCESS',
  SearchProvidersFail = '[SEARCH_PROVIDER] FAILED'
}

export class ProviderSearchAction implements Action {
  type: string;
  payload: {
    isRequesting: boolean,
    providers: Array<IProviderSearchObject>,
    error: boolean,
    searchPhrase: string
  };
}

export class SearchProvidersRequest implements Action {
  readonly type = ProviderSearchActionTypes.SearchProvidersRequest;
  constructor(readonly payload: {isRequesting: boolean, searchPhrase: string}) {}
}

export class SearchProvidersSuccess implements Action {
  readonly type = ProviderSearchActionTypes.SearchProvidersSuccess;
  constructor(readonly payload: {isRequesting: boolean, providers: Array<IProviderSearchObject>}) {}
}

export class SearchProvidersFail implements Action {
  readonly type = ProviderSearchActionTypes.SearchProvidersFail;
  constructor(readonly payload: {error: boolean}) {}
}

export type ProviderSearchActions = SearchProvidersRequest | SearchProvidersSuccess | SearchProvidersFail;

reducer file

import {IProviderSearchObject} from '../../models/provider.type';
import {ProviderSearchAction, ProviderSearchActionTypes} from '../actions/provider-search.action';

export interface IProviderSearchState {
  isRequesting: boolean;
  providers: Array<IProviderSearchObject> | null;
  error: boolean;
}

const initialProviderSearchState: IProviderSearchState = {
  isRequesting: false,
  providers: null,
  error: false
};

export function providerSearchReducer(state: IProviderSearchState = initialProviderSearchState, action: ProviderSearchAction): IProviderSearchState {
  console.log(action, state);
  switch (action.type) {
    case ProviderSearchActionTypes.SearchProvidersRequest:
      return {
        isRequesting: true,
        providers: null,
        error: false
      };
    case ProviderSearchActionTypes.SearchProvidersSuccess:
      return {
        isRequesting: false,
        providers: action.payload.providers,
        error: false
      };
    case ProviderSearchActionTypes.SearchProvidersFail:
      return {
        isRequesting: false,
        providers: null,
        error: true
      }
    default:
      return state;
  }
}

import {ActionReducerMap, MetaReducer} from '@ngrx/store';
import {IProviderSearchState, providerSearchReducer} from './provider-search.reducer';

export interface IAppState {
  providerSearch: IProviderSearchState;
}

export const reducers: ActionReducerMap<IAppState> = {
  providerSearch: providerSearchReducer
};

export const selectProviderSearch = (state: IAppState) => state.providerSearch.providers;

export const metaReducers: MetaReducer<any>[] = [];

Effects file

import {Actions, Effect, ofType} from '@ngrx/effects';
import {IAppState} from '../reducers';
import {ProviderSearchService} from '../../modules/provider-search/services/provider-search.service';
import {Store} from '@ngrx/store';
import {ProviderSearchActionTypes, SearchProvidersSuccess, SearchProvidersFail} from '../actions/provider-search.action';
import {catchError, map, switchMap} from 'rxjs/operators';
import { of } from 'rxjs';
import {IProviderSearchObject} from '../../models/provider.type';
import {Injectable} from '@angular/core';

@Injectable()
export class ProviderSearchEffects {

  constructor(private actions$: Actions,
              private store: Store<IAppState>,
              private providerSearchService: ProviderSearchService) {}

  @Effect()
  searchProvider$ = this.actions$
    .pipe(
      ofType<any>(ProviderSearchActionTypes.SearchProvidersRequest),
      map(action => action.payload),
      switchMap((action) => {
        return this.providerSearchService.getProviderByPhrase(action.searchPhrase).pipe(
          map((data: Array<IProviderSearchObject>) => new SearchProvidersSuccess({isRequesting: false, providers: data})),
          catchError(error => of(new SearchProvidersFail({error: true})))
        );
      })
    );
}

import { ProviderSearchEffects } from './provider-search.effects';
export const effects: Array<any> = [ProviderSearchEffects];

Service file

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from './../../../../environments/environment';
import { Store } from '@ngrx/store';
import * as ProviderSearchAction from '../../../store/actions/provider-search.action';
import {IAppState} from '../../../store/reducers';
import {Observable} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ProviderSearchService {

  constructor(
    private http: HttpClient,
    private store: Store<IAppState>
  ) { }

  public getProviderByPhrase = (searchPhrase: string): Observable<any> => {
    return this.http.get('https://mydummyapi.com?q=' + searchPhrase);
  }
  public searchProviderByTermSearch = (searchPhrase: string): any => {
    return this.store.dispatch(new ProviderSearchAction.SearchProvidersRequest({isRequesting: true, searchPhrase}));
  }
}

Component file

ngOnInit() {
    this.providerSearchControl.valueChanges
    .pipe(
      debounceTime(500),
      tap(() => {
        this.isLoading = true;
      }),
      switchMap((value: string) => this.providerSearchService.searchProviderByTermSearch(value))
      .pipe(
        finalize(() => {
          this.isLoading = false;
        })
      )
      )
    )
    .subscribe((data: Array<IProviderSearchObject>) => {
      console.log(data);
      if (data && data.length > 0) {
        this.providerSearchResult = data;
      }
    });
  }

When the user start typing the autocomplete field then searchProviderByTermSearch method is invoked inside the service file and that dispatches the action.

But after [SEARCH_PROVIDER] SUCCESS call is made nothing is happening.

1

1 Answers

0
votes

A store.dispatch is a void, it does not return a value. The data from the subscribe code, is not an Array<IProviderSearchObject>.

Your flow should be:

  • dispatch fetch
  • call service in effects
  • dispatch fetch success/failure
  • update state via reducer
  • read data with selectors
  • update component

https://ngrx.io/guide/store#diagram