[英]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_ACTION
和SEND_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
. 使用此设置,我可以使用三种类型的服务
FooBarDataService
, FooBarMapsService
和FooBarLogicService
。 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.