簡體   English   中英

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

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

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;
}

問題出在reducer函數中,我不知道如何注入我在不同文件中創建的可注入服務並使用其中的函數。 id部分 - 我需要使用像this.threadService.generateID()這樣的函數生成firebase推送ID ...

但由於這是一個函數,我沒有使用DI的構造函數,我不知道如何在threadService中獲取函數!

沒有為減壓器注入服務的機制。 減速器應該是純粹的功能。

相反,您應該使用ngrx/effects - 這是實現動作副作用的機制。 效果偵聽特定操作,執行一些副作用,然后(可選)發出進一步的操作。

通常,您會將操作拆分為三個:請求; 成功的反應; 和錯誤響應。 例如,您可以使用:

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(),
                // ...
              }
          };
        }
      });
  }
}

而不是與服務交互,您的reducer將是一個純函數,只需要處理SEND_NEW_MESSAGE_RES_ACTIONSEND_NEW_MESSAGE_ERR_ACTION來執行適當的成功或錯誤有效負載。

效果是基於可觀察的,因此合並同步,基於承諾或基於可觀察的服務是直截了當的。

ngrx/example-app有一些效果

關於您在評論中的查詢:

.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>類型的屬性。 如果您正在查看其他示例,則可能會遇到兩種形式。

經過一段時間的思考,我想出了這個想法:如果我有一個充滿純函數的服務,我不想保留在角度之外的全局變量中,如下所示:

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來獲取我需要的服務的實例。 請記住,在主應用程序上線之前調用此注入器,因此確實需要保持良好狀態並避免在這些服務中保持狀態。 當然還因為減速器必須是純凈的(為了你自己的理智)。

// <!> 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;
    }

}

使用此設置,我可以使用三種類型的服務FooBarDataServiceFooBarMapsServiceFooBarLogicService 數據服務調用webapi並從狀態存儲中提供帶有結果的可觀察數據。 地圖服務用於將foos映射到條形圖,邏輯服務用於在單獨的圖層中添加業務邏輯。 通過這種方式,我可以使用微型控制器,這些控制器僅用於將對象粘合在一起並將它們提供給模板。 控制器中幾乎沒有邏輯。 作為最后的觸摸,解析器可以在路線中提供狀態存儲數據,從而完全抽象出狀態存儲。

關於ReflexiveInjector更多細節在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM