使用angularfire2(v5)和ngrx效果

时间:2017-11-16 21:56:30

标签: firebase angularfire2 ngrx

我必须告诉你我已经疯了。我尝试使用AngularFire2(v.5)从Firebase获取数据,然后在@ngrx / effects上使用它并将其存储在@ngrx / store上。好吧,因为我需要带键的数据,我的效果代码如下所示:

spaces.effects.ts

@Effect()
  getSpaces$ = this.actions$.ofType(SpacesActions.GET_SPACES_REQUEST)
  .switchMap((action: SpacesActions.GetSpacesRequest) => {
    return this.afs.list<Space>('/spaces').snapshotChanges()
      .switchMap(actions => {
      console.log('action is ', actions);
      return actions.map(space => {
      const $key = space.payload.key;
      const data: Space = { $key, ...space.payload.val() };
      console.log('snapshot is: ', data);
      return new SpacesActions.GetSpacesSuccess(data);
      });
    }
  );

我的行动&#34;随附数据和密钥,然后我得到每个项目的密钥,因为这样我就可以轻松更新和删除项目。我的数据库有3个项目,3个键。如果我运行此代码并记录它,首先我可以看到1个数组中的所有项目及其有效负载,而第二个日志我将每个有效负载视为快照。

当我调用GetSpacesSuccess时,我想发送我得到的所有快照(带有密钥和项目)然后存储它。我现在做的方式是3次调度此动作,我只能在屏幕上看到2个项目,因为第一个项目被第二个项目覆盖。

所以,有两个问题:是否有更简单的方法从firebase获取项目的密钥,然后用@ngrx存储它们?如果没有,我错误的是我的第一个项目被覆盖并且我的行动被派遣了3次?

拜托,我在学习的过程中尽我所能。谢谢!

spaces.reducers.ts

    case SpacesActions.GET_SPACES_REQUEST:
    return {
      state,
      spaces: null,
      loading: true
  };
    case SpacesActions.GET_SPACES_SUCCESS:
    return {
      ...state,
      ...action.payload,
      spaces: [state, action.payload],
      loading: false
    };

spaces.actions.ts

export class GetSpacesRequest implements Action {
  readonly type = GET_SPACES_REQUEST;

}

export class GetSpacesSuccess implements Action {
  readonly type = GET_SPACES_SUCCESS;

  constructor(public payload: Space) {} <<<<<HERE I'D LIKE TO GET THE FULL ARRAY WITH EACH KEY
}

2 个答案:

答案 0 :(得分:1)

感谢@ AndreaM16提供最完整的答案。我经历了夜晚的工作,最后我做了不同的事情。实际上,在学习过程中,我们会犯错误以获取知识。可能你的解决方案比我的好,我会研究,谢谢。如果可能的话,我希望听到您对我的解决方案的评论。

最后,在阅读了大量文档后,我的效果现在就是这个,但我没有任何错误捕获器:

private spacesList = 'spaces/';
@Effect()
  getSpaces$ = this.actions$.ofType(SpacesActions.GET_SPACES_REQUEST)
  .switchMap(payload => this.afs.list(this.spacesList).snapshotChanges()
    .map(spaces => {
      return spaces.map(
          res => {
            const $key = res.payload.key;
            const space: Space = {$key, ...res.payload.val()};
            return space;
          }
    );
    })
    .map(res =>
      new SpacesActions.GetSpacesSuccess(res)
    ));

<强>减速

    case SpacesActions.GET_SPACES_REQUEST:
    return Object.assign({}, state, {
      spaces: null,
      loading: true
  });
    case SpacesActions.GET_SPACES_SUCCESS:
    return Object.assign({}, state, {
      spaces: action.payload,
      loading: false
  });

操作

export class GetSpacesRequest implements Action {
  readonly type = GET_SPACES_REQUEST;

}

export class GetSpacesSuccess implements Action {
  readonly type = GET_SPACES_SUCCESS;

  constructor(public payload: Space[]) {}
}

而且,在我的组件中,我需要列表:

 constructor(private store: Store<fromSpaces.FeatureState>) {}
ngOnInit() {
    this.store.dispatch(new SpacesActions.GetSpacesRequest());
    this.spacesState = this.store.select('spaces');
}

答案 1 :(得分:0)

如果我正确理解了您的问题,您希望为每个项目存储也存储其密钥。您正在寻找Map

我会按如下方式构建您的功能。

<强> spaces.actions.ts

加载空间不需要有效负载,而成功只有一个Space数组。我认为您应该在减速机中构建Map<string,Space>string是您的密钥)。

import { Action } from '@ngrx/store';

/** App Models **/
import { Space } from './space.model';

export const GET_SPACES = '[Spaces] Spaces get';
export const GET_SPACES_SUCCESS = '[Start] Spaces get - Success';

export class GetSpacesAction implements Action {
    readonly type = GET_SPACES;
}

export class GetSpacesActionSuccess implements Action {
    readonly type = GET_SPACES_SUCCESS;
    constructor(public payload: Space[]) {}
}

export type All
    = GetSpacesAction
    | GetSpacesActionSuccess;

<强> spaces.effects.ts

我假设你只需要一个方法来获取空格。如果您需要做其他事情,只需编辑这段代码即可。 spaceService.getSpaces()应该只返回一个Spaces数组。因此,创建一个新的Space模型,并在您的服务上将每个json条目映射到new Space()

import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';

/** rxjs **/
import {map} from 'rxjs/operators/map';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {catchError} from 'rxjs/operators/catchError';

/** ngrx **/
import * as spacesActions from './spaces.actions';

/** App Services **/
import { SpacesService } from './spaces.service';

@Injectable()
export class SpacesEffects {

    @Effect() getSpaces$ = this.actions$
      .ofType(spaceActions.GET_SPACES)
        .pipe(
          mergeMap(() => {
            return this.spaceService.getSpaces()
              .pipe(
                map((spaces) => {
                  return new spacesActions.GetSpacesActionSuccess(spaces);
                }),
                catchError((error: Error) => {
                  // Handle erro here
                })
              );
          })
        )
    ;

    constructor(private spacesService: SpacesService, private actions$: Actions) { }

}

<强> spaces.reducer.ts

在这里,您可以构建地图,还可以创建一个新动作来返回,例如,给定其键的空间。我不认为你在这里需要任何加载参数,我猜你在视图中使用它来进行一些加载处理,只需在你的视图中使用AsyncPipe并处理加载动画并检查*ngIf是否存在空间与否。

/** ngrx **/
import {createFeatureSelector} from '@ngrx/store';
import {createSelector} from '@ngrx/store';
import * as spacesActions from './spaces.actions';

export type Action = spacesActions.All;

/** App Models **/
import { Space } from './space.model';

export interface SpaceState {
    keySpaces: Map<string, Space>;
    spaces: Space[];
    keys: string[];
}

export const initialState: SpaceState = {
    keySpaces: new Map<string, Space>(),
    spaces: [],
    keys: []
};

// Selectors
export const selectSpace  = createFeatureSelector<SpaceState>('space');
export const getKeySpaces = createSelector(selectSpace, (state: StartState) => {
  return state.keySpaces;
});
export const getSpaces = createSelector(selectSpace, (state: SpaceState) => {
  return state.spaces;
});
export const getKeys = createSelector(selectSpace, (state: SpaceState) => {
  return state.keys;
});

export function spacesReducer(state: SpaceState = initialState, action: Action): SpaceState {
    switch (action.type) {
        case startActions.GET_SPACES_SUCCESS:
            // Since we return this from effect
            const fetchedSpaces = action.payload;  
            const fetchedKeys = [];
            const keySpacesMap = new Map<string, Space>();
            fetchedSpaces.forEach( (space: Space) => {
                fetchedkeys = fetchedKeys.concat(space.key);
                keySpacesMap.set(space.key, new Space(space));
            }
            returns {
                ...state,
                keySpaces: keySpacesMap,
                spaces: fetchedSpaces,
                keys: fetchedkeys
            }
        default: {
            return state;
        }
    }
}

最后,你应该能够在你的组件中获得这样的参数,如:

. . .

 keySpaces$ = Observable<Map<string, Space>>;
 spaces$ = Observable<Array<Space>>;
 keys$ = Observable<Array<string>>;

 constructor(private _store: Store<AppState>) {
     this.keySpaces$ = this._store.select(getKeySpaces);
     this.space$s = this._store.select(getSpaces);
     this.keys$ = this._store.select(getKeys);
 }

 . . .

 ngOnInit() {
     this._store.dispatch(new spacesActions.GetSpacesAction);
 }

. . .

当然,将新状态添加到AppState

. . .

export interface AppState {
  . . .
  space: SpaceState;
}