Ngrx/Storeと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;
}
問題はリデューサー関数内にあります。別のファイルで作成した注入可能なサービスを注入し、その中で関数を使用する方法はわかりません。 id部分-this.threadService.generateID()などの関数を使用してfirebaseプッシュIDを生成する必要があります...
しかし、これは関数であるため、DIを使用するコンストラクターがなく、threadService内で関数を取得する方法がわかりません。
レデューサーにサービスを注入するメカニズムはありません。レデューサーは純粋な関数であると想定されています。
代わりに、 _ngrx/effects
_ を使用する必要があります。これは、アクションの副作用を実装するためのメカニズムです。エフェクトは特定のアクションをリッスンし、何らかの副作用を実行してから、(オプションで)さらにアクションを発行します。
通常、アクションは3つに分割されます。リクエスト、成功応答;およびエラー応答。たとえば、次を使用できます。
_SEND_NEW_MESSAGE_REQ_ACTION
SEND_NEW_MESSAGE_RES_ACTION
SEND_NEW_MESSAGE_ERR_ACTION
_
そして、あなたの効果は次のようになります。
_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(),
// ...
}
};
}
});
}
}
_
レデューサーは、サービスと対話するのではなく、成功またはエラーペイロードに対して適切な処理を行うために_SEND_NEW_MESSAGE_RES_ACTION
_および_SEND_NEW_MESSAGE_ERR_ACTION
_を処理するだけの純粋な関数になります。
効果は観測ベースであるため、同期、約束ベース、または観測ベースのサービスを組み込むことは簡単です。
_ngrx/example-app
_には effects がいくつかあります。
コメント内のクエリについて:
.map(toPayload)
は便宜上のものです。 toPayload
は、存在するngrx
関数であるため、_.map
_に渡して、アクションのpayload
を抽出できます。
オブザーバブルベースのサービスの呼び出しは簡単です。通常、次のようなことをします。
_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(),
// ...
}
}))
);
}
_
また、効果は_Observable<Action>
_を返す関数または_Observable<Action>
_型のプロパティとして宣言できます。他の例を見ると、両方のフォームに出くわす可能性があります。
これについてしばらく考えた後、私はこのアイデアを思いつきました:angularのようなグローバル変数に保持したくない純粋な関数でいっぱいのサービスがある場合はどうですか:
export const fooBarService= {
mapFooToBar: (foos: Foo[]): Bar[] => {
let bars: Bar[];
// Implementation here ...
return bars;
}
}
私は依存関係の注入を使用していないことをだれも恐れずに簡単にアプリケーションに渡すことができるように、サービスとしてそれを持ちたいです:
@Injectable()
export class FooBarService{
public mapFooToBar (foos: Foo[]): Bar[] {
let bars: Bar[];
// Implementation here ...
return bars;
}
}
必要なサービスのインスタンスを取得するために、ReflectiveInjector
を使用できます。このインジェクターはメインアプリが公開される前に呼び出されるため、Niceをプレイし、これらのサービスの状態を維持しないようにする必要があります。そしてもちろん、減速機は本当に純粋でなければならないからです(あなた自身の正気のために)。
// <!> 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;
}
}
このセットアップを使用して、3つのタイプのサービスFooBarDataService
、FooBarMapsService
、およびFooBarLogicService
を使用できます。データサービスはwebapiを呼び出し、状態ストアからオブザーバブルに結果を提供します。マップサービスはfooをバーにマップするために使用され、ロジックサービスはビジネスロジックを別のレイヤーに追加するために使用されます。これにより、オブジェクトをつなぎ合わせてテンプレートに提供するためだけに使用される小さなコントローラーを使用できます。コントローラにはほとんどロジックがありません。最後の仕上げとして、リゾルバーは状態ストアのデータをルートに提供し、状態ストアを完全に抽象化します。
ReflexiveInjector
here の詳細。