The effect – listening to a specific dispatched action

So we come to the effect. Our effect acts like a listener to a dispatched action. This gives us the opportunity to carry out a unit of work, but also to dispatch an action once that work is done. 

We have created all the usual bits that we are used to, so now it is time to create our effect that will handle the entire workflow:

// product/product.effect.ts

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

@Injectable()
export class ProductEffects {
@Effect() products$: Observable<Action>;

constructor(
private actions$: Actions<Action>>
) {}
}

The effect is just a class decorated with the @Injectable decorator. It also contains two members: one member of Actions type and another of the Observable<Action> type. Actions come from the @ngrx/effects module and are nothing more than a specialized Observable with the ofType() method on it. ofType() is the method that takes a string constant, which is the event we are listening for. In the previous code, the products$ is the Observable that we decorate with the @Effect decorator. Our next step is to connect products$ with actions$, and define how our effect should work. We do that with the following code:

// product/product.effect.ts, starting out..

import { Actions, Effect, ofType } from "@ngrx/effects";
import { switchMap } from "rxjs/operators";
import { Observable } from "rxjs/Observable";
import { Injectable } from "@angular/core";

@Injectable()
export class ProductEffects {
@Effect() products$: Observable<Action> = this.actions$.pipe(
ofType(FETCHING_PRODUCTS),
switchMap(action => {
// do something completely else that returns an Observable
})
);


constructor(
private actions$: Actions<Action>>
) {}
}

Ok, so we have set up our effect a little more. The call to ofType() ensures we set ourselves up to listen to a specific dispatched action. The call to switchMap() ensures we are able to take the current Observable that we are currently on, and turn it into something completely different, such as a call to an AJAX service. 

Let's now return back to our example and see how we can fit in some product-related logic in there:

// product/product.effect.ts

import { Actions, Effect, ofType } from "@ngrx/effects";
import { HttpClient } from "@angular/common/http";
import { FETCHING_PRODUCTS } from "./product.constants";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { delay, map, catchError, switchMap } from "rxjs/operators";
import { fetchProductsSuccessfully, fetchError } from "./product.actions";
import { Action } from "@ngrx/store";

@Injectable()
export class ProductEffects {
@Effect()
products$ = this.actions$.pipe(
ofType(FETCHING_PRODUCTS),
switchMap(action =>
this.http
.get("data/products.json")
.pipe(

delay(3000),
map(fetchProductsSuccessfully),

catchError(err => of(fetchError(err)))
)

)
);

constructor(private actions$: Actions<Action>, private http: HttpClient) {}
}

What we do in the preceding code is listen to our FETCHING_PRODUCTS action and carry out a call to an AJAX service. We added a call to the delay() operator so as to simulate that our AJAX call takes some time to carry out. This will give us a chance to show a loading spinner. The map() operator ensures we dispatch an action when we get the AJAX response back. We can see that we call the action creator, fetchProductsSuccessfully(), which implicitly calls the reducer and sets a new state on the products property. 

At this point, we need to register the effect before moving on. We can do so in the root module or in the feature module. It's a very similar call, so let's describe both ways of doing it:

// app.module.ts - registering our effect in the root module, alternative I

/* omitting the other imports for brevity */
import { EffectsModule } from "@ngrx/effects";
import { ProductEffects } from "./products/product.effect";


@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
StoreModule.forRoot({}),
ProductsModule,
StoreDevtoolsModule.instrument({
maxAge: 25 // Retains last 25 states
}),
EffectsModule.forRoot([ ProductEffects ])
],
bootstrap: [AppComponent]
})
export class AppModule {}

If we have a feature module, on the other hand, we could be using the forFeature() method on the EffectsModule and call that in our feature module like so:

// product/product.module.ts, registering in the feature module, alternative II

import { NgModule } from "@angular/core";
import { ProductComponent } from "./product.component";
import { BrowserModule } from "@angular/platform-browser";
import { ProductEffects } from "./product.effect";
import { EffectsModule } from "@ngrx/effects";
import { StoreModule, Action } from "@ngrx/store";
import { ProductReducers } from "./product.reducer";
import { HttpClientModule } from "@angular/common/http";
import { ActionReducerMap } from "@ngrx/store/src/models";

@NgModule({
imports: [
BrowserModule,
StoreModule.forFeature("featureProducts", ProductReducers),
EffectsModule.forFeature([ProductEffects]),
HttpClientModule
],
exports: [ProductComponent],
declarations: [ProductComponent],
providers: []
})
export class ProductModule {}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.230.121