简体   繁体   English

如何在ngrx / Store reducer中使用其他Angular2服务?

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

New to both ngrx/Store and reducer. ngrx / Store和reducer都是新手。 Basically, I have this reducer: 基本上,我有这个减速器:

import {StoreData, INITIAL_STORE_DATA} from "../store-data";
import {Action} from "@ngrx/store";
import {
  USER_THREADS_LOADED_ACTION, UserThreadsLoadedAction, SEND_NEW_MESSAGE_ACTION,
  SendNewMessageAction
} from "../actions";
import * as _ from "lodash";
import {Message} from "../../shared-vh/model/message";
import {ThreadsService} from "../../shared-vh/services/threads.service";

export function storeData(state: StoreData = INITIAL_STORE_DATA, action: Action): StoreData {


  switch (action.type) {

    case SEND_NEW_MESSAGE_ACTION:
      return handleSendNewMessageAction(state, action);

    default:
      return state
  }
}

function handleSendNewMessageAction(state:StoreData, action:SendNewMessageAction): StoreData {

  const newStoreData = _.cloneDeep(state);

  const currentThread = newStoreData.threads[action.payload.threadId];

  const newMessage: Message = {
    text: action.payload.text,
    threadId: action.payload.threadId,
    timestamp: new Date().getTime(),
    participantId: action.payload.participantId,
    id: [need a function from this service: ThreadsService]
  }

  currentThread.messageIds.push(newMessage.id);

  newStoreData.messages[newMessage.id] = newMessage;

  return newStoreData;
}

The problem is within the reducer function, I do not know how to inject an injectable service I created in a different file and use the function within it. 问题出在reducer函数中,我不知道如何注入我在不同文件中创建的可注入服务并使用其中的函数。 The id part - I need to generate a firebase push ID using function like this.threadService.generateID() ... id部分 - 我需要使用像this.threadService.generateID()这样的函数生成firebase推送ID ...

But since this is a function, I do not have a constructor to use DI and I have no idea how to get functions within threadService! 但由于这是一个函数,我没有使用DI的构造函数,我不知道如何在threadService中获取函数!

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. 相反,您应该使用ngrx/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. 而不是与服务交互,您的reducer将是一个纯函数,只需要处理SEND_NEW_MESSAGE_RES_ACTIONSEND_NEW_MESSAGE_ERR_ACTION来执行适当的成功或错误有效负载。

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 . ngrx/example-app有一些效果

Regarding your queries in the comments: 关于您在评论中的查询:

The .map(toPayload) is just for convinience. .map(toPayload)只是为了方便。 toPayload is an ngrx function that exists so it can be passed to .map to extract the action's payload , that's all. toPayload是一个存在的ngrx函数,因此它可以传递给.map来提取动作的payload ,就是这样。

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> . 此外,效果可以声明为返回Observable<Action>函数或Observable<Action>类型的属性。 If you are looking at other examples, you are likely to come across both forms. 如果您正在查看其他示例,则可能会遇到两种形式。

After thinking a while about this I came up with this idea: What if I have a service full of pure functions which I don't want to keep in a global variable outside of angular like this: 经过一段时间的思考,我想出了这个想法:如果我有一个充满纯函数的服务,我不想保留在角度之外的全局变量中,如下所示:

export const fooBarService= {
    mapFooToBar: (foos: Foo[]): Bar[] => {
        let bars: Bar[];
        // Implementation here ...
        return bars;
    } 
}

I would like to have it as a service so I can easily pass it in the application without anybody freaking out that I don't use dependency injection: 我想把它作为一项服务,所以我可以轻松地在应用程序中传递它,而没有任何人吓到我不使用依赖注入:

@Injectable()
export class FooBarService{
    public mapFooToBar (foos: Foo[]): Bar[] {
        let bars: Bar[];
        // Implementation here ...
        return bars;
    } 
}

I can use ReflectiveInjector in order to get an instance of the service I need. 我可以使用ReflectiveInjector来获取我需要的服务的实例。 Keep in mind that this injector is invoked before the main app is going live so it's really necessary to play nice and avoid keeping state in these services. 请记住,在主应用程序上线之前调用此注入器,因此确实需要保持良好状态并避免在这些服务中保持状态。 And of course also because reducers really have to be pure (for your own sanity). 当然还因为减速器必须是纯净的(为了你自己的理智)。

// <!> Play nice and use only services containing pure functions
var injector = ReflectiveInjector.resolveAndCreate([FooBarService]);
var fooBarService= injector.get(FooBarService);

// Due to changes in ngrx4 we need to define our own action with payload
export interface PayloadAction extends Action {
    payload: any
}

/**
 * Foo bar reducer
 */
export function fooBarReducer(
    state: FooBarState = initialState.fooBar, 
    action: PayloadAction
) {
    switch (action.type) {

        case fooBarActions.GET_FOOS_SUCCESS:
            return Object.assign({}, state, <FooBarState>{
                foos: action.payload,
                // No effects used, all nicelly done in the reducer in one shot
                bars: fooBarService.mapFooToBar (action.payload) 

            });

        default:
            return state;
    }

}

Using this setup I can use three types of service FooBarDataService , FooBarMapsService and FooBarLogicService . 使用此设置,我可以使用三种类型的服务FooBarDataServiceFooBarMapsServiceFooBarLogicService Data service calls the webapi and provides observables from the state store with the results. 数据服务调用webapi并从状态存储中提供带有结果的可观察数据。 Map service is used to map foos to bars and Logic service is used to add the business logic in a separate layer. 地图服务用于将foos映射到条形图,逻辑服务用于在单独的图层中添加业务逻辑。 This way I can have tiny controllers that are used only to glue objects together and serve them to the templates. 通过这种方式,我可以使用微型控制器,这些控制器仅用于将对象粘合在一起并将它们提供给模板。 Almost no logic in controllers. 控制器中几乎没有逻辑。 And as a final touch, the resolvers can provide the state store data in the routes thus abstracting away the state store completely. 作为最后的触摸,解析器可以在路线中提供状态存储数据,从而完全抽象出状态存储。

More details about ReflexiveInjector here . 关于ReflexiveInjector更多细节在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 ngrx/Store 减速器中使用其他 Angular11 服务作为初始 state? - How to use other Angular11 service inside an ngrx/Store reducer as an initial state? 如何使用Angular2 ngrx storemodule reducer(initialState)注入服务? - how to use Angular2 ngrx storemodule reducer (initialState) inject service? ngrx store reducer配置(角度5) - ngrx store reducer configuration (angular 5) Angular2 @ ngrx / store / effects - 在哪里调用服务函数 - Angular2 @ngrx/store / effects - where to call service functions 如何为Angular ngrx商店制作一个“reducer enhancer”来进行撤销/重做 - How to make a “reducer enhancer” for Angular ngrx store for undo / redo Angular 8 Ngrx存储如何在没有订阅回调的情况下获取reducer的值? - Angular 8 ngrx store how to get reducer value without subscribe callback? 如何设置@ ngrx / store的reducer到设置{[key:string]:AnyComponent}的角度6中 - How to set reducer for @ngrx/store in angular 6 that set {[key:string]:AnyComponent} 如何修改NGR存储不在reducer中? - How to modify NGRX store not in reducer? 如何使用store.select()在Angular2中使用ngrx获取根级别状态? - How to use store.select() to get the root level state using ngrx in Angular2? 如何在 NgRx 7 减速器中使用 NgRx 8 动作 - How to use NgRx 8 actions with NgRx 7 reducer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM