How to use other Angular2 service inside an ngrx/Store reducer?

There is no mechanism for injecting services into reducers. Reducers are supposed to be pure functions.

Instead, you should use ngrx/effects – which is the mechanism for implementing action side-effects. Effects listens for particular actions, perform some side-effect and then (optionally) emit further actions.

Typically, you would split your action into three: the request; the success response; and the error response. For example, you might use:

SEND_NEW_MESSAGE_REQ_ACTION
SEND_NEW_MESSAGE_RES_ACTION
SEND_NEW_MESSAGE_ERR_ACTION

And your effect would look something like this:

import { Injectable } from "@angular/core";
import { Actions, Effect, toPayload } from "@ngrx/effects";
import { Action } from "@ngrx/store";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/map";

@Injectable()
export class ThreadEffects {

  constructor(
    private actions: Actions,
    private service: ThreadsService
  ) {}

  @Effect()
  sendNewMessage(): Observable<Action> {

    return this.actions
      .ofType(SEND_NEW_MESSAGE_REQ_ACTION)
      .map(toPayload)
      .map(payload => {
        try {
          return {
              type: SEND_NEW_MESSAGE_RES_ACTION,
              payload: {
                  id: service.someFunction(),
                  // ...
              }
          };
        } catch (error) {
          return {
              type: SEND_NEW_MESSAGE_ERR_ACTION
              payload: {
                error: error.toString(),
                // ...
              }
          };
        }
      });
  }
}

Rather than interacting with the service, your reducer would then be a pure function that would need only to handle the SEND_NEW_MESSAGE_RES_ACTION and SEND_NEW_MESSAGE_ERR_ACTION to do something appropriate with the success or error payloads.

Effects are observable-based, so incorporating synchronous, promise-based or observable-based services is straight forward.

There are some effects in the ngrx/example-app.

Regarding your queries in the comments:

The .map(toPayload) is just for convinience. toPayload is an ngrx function that exists so it can be passed to .map to extract the action’s payload, that’s all.

Calling a service that’s observable-based is straight-forward. Typically, you’d do something like this:

import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/of";
import "rxjs/add/operator/catch";
import "rxjs/add/operator/map";
import "rxjs/add/operator/switchMap";

@Effect()
sendNewMessage(): Observable<Action> {

  return this.actions
    .ofType(SEND_NEW_MESSAGE_REQ_ACTION)
    .map(toPayload)
    .switchMap(payload => service.someFunctionReturningObservable(payload)
      .map(result => {
        type: SEND_NEW_MESSAGE_RES_ACTION,
        payload: {
          id: result.id,
          // ...
        }
      })
      .catch(error => Observable.of({
        type: SEND_NEW_MESSAGE_ERR_ACTION
        payload: {
          error: error.toString(),
          // ...
        }
      }))
    );
}

Also, effects can be declared as functions returning Observable<Action> or as properties of type Observable<Action>. If you are looking at other examples, you are likely to come across both forms.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)