Resort Effects
With the resort actions, reducer function and selectors declared we are now ready to create the ResortEffects class with the side effect for searching resorts using the API.
ResortEffects Class
Create the file src/app/state/resort/resort.effects.ts and declare and export the ResortEffects class:
import { Injectable } from '@angular/core';
import { ResortService } from '@app/core/services/resort.service';
import { Actions } from '@ngrx/effects';
@Injectable()
export class ResortEffects {
constructor(private actions: Actions, private resortService: ResortService) {}
}- Decorate the
ResortEffectsclass with the@Injectabledecorator. - Inject the
Actionsobservable in the constructor. - Inject the
ResortServicein the constructor. - We'll use the
actionsobservable to listen for a specifictypethat is dispatched.
Review ResortService
Let's quickly review the ResortService class located at src/app/core/services/resort.service.ts:
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Resort } from '@app/models/resort.model';
import { Observable } from 'rxjs';
import { BaseService } from './base.service';
@Injectable({
providedIn: 'root'
})
export class ResortService extends BaseService {
constructor(private httpClient: HttpClient) {
super();
}
search(q: string): Observable<Resort[]> {
return this.httpClient.get<Resort[]>(`${this.BASE_URL}/resorts`, {
params: new HttpParams().set('name_like', `^${q}`)
});
}
}- Note the
search()method requires the user's query, declared asq. - The
search()method returns anObservableof an array ofResortobjects.
Declare search Effect
Declare a search property that invokes the ResortService.search() method:
import { Injectable } from '@angular/core';
import { ResortService } from '@app/core/services/resort.service';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { catchError, exhaustMap, map } from 'rxjs/operators';
import {
ResortActionTypes,
SearchResorts,
SearchResortsFail,
SearchResortsSuccess
} from './resort.actions';
@Injectable()
export class ResortEffects {
@Effect()
search: Observable<Action> = this.actions.pipe(
ofType<SearchResorts>(ResortActionTypes.SearchResorts),
exhaustMap(action =>
this.resortService.search(action.q).pipe(
map(resorts => new SearchResortsSuccess(resorts)),
catchError(error => of(new SearchResortsFail(error)))
)
)
);
constructor(private actions: Actions, private resortService: ResortService) {}
}- Decorate the
searchproperty with@Effect. This instructs the NgRx effects library that this property is a side effect that should be subcribed to. - The
searchproperty returns anObservableof anAction. This allows us to chain actions. The NgRx effects library will automatically dispatch the action (or actions). - Use the
exhaustMapoperator to map to the innerObservablereturned from thesearch()method. - The
exhaustMap()operator will discard any notifications from the outer observable until the inner observable completes. - Invoke the
search()method on theresortServiceinstance providing the user's query. - Use the
pipe()method tomap()the next notification to theSearchResortsSuccessaction, specifying theresortsarray. - If an error occurs we catch it via the
catchError()operator and return a new observableof()theSearchResortsFailaction.
Pro Tip: We use the of() creation operator to create an Observable that immediately emits a next notification with the value specified.
In this case, the SearchResortsFail action.
Import EffectsModule
Open src/app/state/state.module.ts and add the EffectsModule to the imports array for the module:
import { ResortEffects } from '@app/state/resort/resort.effects';
import { EffectsModule } from '@ngrx/effects';
@NgModule({
imports: [
// code omitted
EffectsModule.forRoot([ResortEffects])
]
})
export class StateModule {}