【问题标题】:NGRX 8 reducer returning Object instead of ArrayNGRX 8减速器返回对象而不是数组
【发布时间】:2019-12-18 18:32:03
【问题描述】:

reducer 返回的数据是一个对象,但我需要它是一个数组。 我已经尝试返回 action.recentSearches 但它似乎不起作用。

正在返回的数据是:

{ "loading": false, "recentSearches": [ { "corpId": "123", "site": "location", "building": "location", "floor": "2N" }, { "corpId": "123", "site": "location", "building": "location", "floor": "09" }, { "corpId": "123", "site": "location", "building": "location", "floor": "01" } ] }`

行动:

  export const getRecentSearches = createAction(
'[ConfRoom API] Request Recent Searches'
);

export const getRecentSearchesloadSuccess = createAction('[ConfRoom API] Recent Searches Load Success', props<{recentSearches: RecentSearchesModel[]}>());

还原器: console.log 确实打印了我需要的值,但返回 action.recentSearches 不起作用

export const initialState = [];
const _confRoomReducer = createReducer(
initialState,
on(confRoomActionTypes.getRecentSearches,  state=> ({      
  ...state
})),
on(confRoomActionTypes.getRecentSearchesloadSuccess,(state, { recentSearches }) => ({ ...state, recentSearches })) )

  export function confRoomReducer(state, action) {
  console.log(action.recentSearches);
return _confRoomReducer(state, action); 

组件中的值

recentSearches$: Observable<RecentSearchesModel[]> = this.store.select(state => state.recentSearches); 

更新:

对减速器进行了 2 次编辑:

export const state = [];
export const initialState = [];


const _confRoomReducer = createReducer(
initialState,
on(confRoomActionTypes.getRecentSearches,  state=>     
 state
),
on(confRoomActionTypes.getRecentSearchesloadSuccess,(state,  {recentSearches} ) => ([ ...state, recentSearches ])) )

 export function confRoomReducer(state, action) {
return _confRoomReducer(state, action); 

数据现在正在像这样返回,但我需要摆脱外部 [] 我用 [] 包装数据响应,在减速器中的某个点,但这是我最接近它的功能正确:

    [ [ { "corpId": "123", "site": "location", "building": "location, "floor": "01,02" }, { "corpId": "123", "site": "location", "building": "location", "floor": "01,02" } ] ]

我的 ngFor 可以读取数据,但它没有按预期工作,因为它需要我添加要显示的数据的索引:

 <tr *ngFor="let recentSearch of recentSearches$ | async; let i = index" ng-class-odd="'striped'">
  <td>{{recentSearch[0]}}, {{recentSearch[0].building}}, {{recentSearch[0].floor}}</td>
</tr>

更新 通过向减速器添加推荐的更改,我能够提出解决方案,但我不确定这是访问我需要的数据的正确方法

减速器

export interface RecentSearchesModel {
site: string;
corpId: string;
building: string;
floor: string
}

export interface State {
  resultList: RecentSearchesModel[];
}

const initialState: State = {
resultList: []
};


const _confRoomReducer = createReducer(
initialState,
on(confRoomActionTypes.getRecentSearches, state => ({
...state
})),
on(
confRoomActionTypes.getRecentSearchesloadSuccess,
(state, { recentSearches }) => ({ ...state, resultList: recentSearches })
  )
);

export function confRoomReducer(state, action) {
  return _confRoomReducer(state, action);
}

数据 现在数据是这样返回的

{ "resultList": [ { "corpId": "123", "site": "CHINA", "building": "BUILDING 12", "floor": "2N" }, { "corpId": "123", "site": "US", "building": "BIG BUILDING", "floor": "09" }, { "corpId": "123", "site": "LONDON", "building": "BIG BEN", "floor": "01" } ] }

但要在组件中访问我想要的数据,我必须编辑模型并添加 resultList[]

export interface RecentSearchesModel {
  corpId: string;
  site: string;
  building: string;
  floor: string;
resultList[];
}

我觉得我不应该将 resultList 添加到我的模型中,因为数据从未真正映射到它,我只是使用它来访问与数据关联的标签 组件

recentSearches$: Observable<RecentSearchesModel[]> = this.store.select(state => state.recentSearches.resultList); 

【问题讨论】:

  • 尝试 [...state] 而不是 {...state}
  • 您在使用实体适配器吗?请在上面的示例中包含您的 State 接口和 initialState
  • 嗨,我已经更新以显示完整的减速器文件,我是 ngrx 的新手,所以我可能会错误地实施这些
  • 嗨,我已经做出了建议的更改,并为我的问题提出了解决方案,我已经对我的问题进行了更新以反映这一点,非常感谢你对此的看法@JamesD
  • 正如您将在我提供的示例应用程序链接中看到的那样,您将在底部看到如何制作选择器,您正在导出一个函数,但您应该通过选择器传递数据。我建议您克隆该示例应用程序并运行它。如果您遵循它,您会发现如何使用最新版本正确实现 ngrx。所有选择器都构建在一个主减速器文件 ref 中。 github.com/ngrx/platform/blob/master/projects/example-app/src/… 是这些选择器,您将专门调用这些选择器来获取所需的状态片段,在您的情况下为 resultList

标签: angular typescript ngrx angular8 state-management


【解决方案1】:

你应该看看ngrx团队给出的example app

如果您尝试存储前端提供给您的数据,则说明您初始化错误

您应该有一个用于会议室列表的界面

// have a model file:
export interface ConfRoom{
    corpId: number;
    site: string;
    location: string;
    floor: string;
}

// in your reducer
export interface State {
  confList: ConfRoom[];
}
const initialState: State = {
  confList: []
};


const _confRoomReducer = createReducer(
initialState,
// this line does nothing and can be delete...
// on(confRoomActionTypes.getRecentSearches,  state=>     
//  state
// ),
// if recentSearches should change confList do this
on(confRoomActionTypes.getRecentSearchesloadSuccess,(state,  {recentSearches} ) => ( {...state, confList: recentSearches})) )

// if recentSearches should be added to confList do this
on(confRoomActionTypes.getRecentSearchesloadSuccess,(state,  {recentSearches} ) => ( {...state, confList: [...confList, recentSearches]})) )

如果您尝试获取后端数据: 下面是一个使用api调用和填充数据的例子。

这是一个只有负载的简单减速器:

import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
import { createReducer, on } from '@ngrx/store';

import {
  ProjectCollectionApiActions,
  ProjectCollectionActions
} from '../../actions';
import { Project } from 'src/app/core/models';

export interface State extends EntityState<Project> {
  loading: boolean;
  loaded: boolean;
}

export const adapter: EntityAdapter<Project> = createEntityAdapter<Project>({
  selectId: (project: Project) => project.id,
  sortComparer: false,

});

export const initialState: State = adapter.getInitialState({
  loading: false,
  loaded: false
});

export const reducer = createReducer(
  initialState,
  on(ProjectCollectionActions.loadProjectCollection, (state) => ({
    ...state,
    loading: true,
  })),
  on(ProjectCollectionApiActions.loadProjectsSuccess,
    (state, { projects }) => adapter.addMany(projects, {
      ...state,
      loading: false,
      loaded: true
    })
  ),
);

export const getLoaded = (state: State) => state.loaded;

export const getLoading = (state: State) => state.loading;

我将它导入一个名为 index.ts 的主文件中


import {
  createSelector,
  createFeatureSelector,
  combineReducers,
  Action,
} from '@ngrx/store';

import * as fromDates from './reports/dates.reducer';
import * as fromSuperIntendents from './superintendents/superintendent.reducer';
import * as fromReports from './reports/reports.reducer';
import * as fromProjects from './projects/projects.reducer';
import * as fromMachines from './machines/machines.reducer';

import * as fromLaborers from './laborers/laborers.reducer';
import * as fromCollection from './reports/collection.reducer';
import * as fromRoot from '../../../state/reducers';
import { generateMockReport, Report } from 'src/app/reports/models';

export interface DataState {
  dates: fromDates.State;
  superintendents: fromSuperIntendents.State;
  reports: fromReports.State;
  laborers: fromLaborers.State;
  collection: fromCollection.State;
  projects: fromProjects.State;
  machines: fromMachines.State;
}

export interface State extends fromRoot.State {
  data: DataState;
}

export function reducers(state: DataState | undefined, action: Action) {
  return combineReducers({
    dates: fromDates.reducer,
    superintendents: fromSuperIntendents.reducer,
    reports: fromReports.reducer,
    laborers: fromLaborers.reducer,
    collection: fromCollection.reducer,
    projects: fromProjects.reducer,
    machines: fromMachines.reducer
  })(state, action);
}



export const getDataState = createFeatureSelector<State, DataState>('data');

//here all my other reducers come
//...
//

export const getProjectsState = createSelector(
  getDataState,
  (state: DataState) => state.projects
);
export const {
  selectIds: getProjectIds,
  selectEntities: getProjectEntities,
  selectAll: getAllProjects,
  selectTotal: getTotalProjects,
} = fromProjects.adapter.getSelectors(getProjectsState); 

export const getProjectsLoaded = createSelector(
  getProjectsState,
  fromProjects.getLoaded
);

export const getLoadedProjectIds = createSelector(
  getProjectIds,
  (ids) => { return ids as string[] }
)

export const getLoadedProjects = createSelector(
  getProjectEntities,
  getLoadedProjectIds,
  (entities, ids) => {
      return ids
          .map(id => entities[id])
  }
);

这给了我这样的结果:

参考效果页面:

import { Injectable } from '@angular/core';

import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';

import {
  ProjectCollectionApiActions,
  ProjectCollectionActions
} from '../../actions';

import { Project } from 'src/app/core/models';
import { LoggingService } from 'src/app/core/services/logging.service';
import { Update } from '@ngrx/entity';
import { ProjectService } from 'src/app/core/services/project.service';

@Injectable()
export class ProjectCollectionEffects {

  loadProjectCollection$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProjectCollectionActions.loadProjectCollection),
      switchMap(() => {
        return this.projectService.getList().pipe(
          map((projects: Project[]) =>
          ProjectCollectionApiActions.loadProjectsSuccess({ projects })
          ),
          catchError(error => {
            this.logging.log('Error in loadCollection effect"',"Project load collection effect - within Approvals")
            return of(ProjectCollectionApiActions.loadProjectsFailure({ error: {...error} }))
          })
        )
      })
    )
  );  


  constructor(
    private actions$: Actions,
    private projectService: ProjectService,
    private logging: LoggingService
  ) {}
}

编辑 如果您没有 Id,您可以通过向服务中的数据添加一个来伪造一个:

let counter = 0; 
return this.http.get<ConfRoom[]>(${url},httpOptions).pipe( 
    map((data: any) => { 
        let result: confRoom[] = []; 
        if(Array.isArray(data)){ 
            data.forEach(room=> { 
               result.push({...room, id: counter}); 
               counter = counter +1; 
            }); 
        } 
        return result; 
    }),
   catchError(handleError)
)

虽然听起来你只需要我展示的第一部分,但我提到你初始化错误? (第一块代码)

【讨论】:

  • 谢谢您的回复,现在才回到这个,我在返回的数据中没有唯一标识符.....如何编辑 entityAdapter 来处理这种情况?
  • 您可以通过将数据映射到具有唯一 ID 的接口来在服务中为其添加假 ID。见上面的编辑
  • 但听起来你只需要我给你的第一段代码
【解决方案2】:

你作为对象返回,但它的初始状态是 [] 如下更改

 on(confRoomActionTypes.getRecentSearches,  state=> state)

【讨论】:

  • 抱歉,我误解了您的回答,是的,这让我非常接近我需要的内容,我将在问题中添加更新
猜你喜欢
  • 1970-01-01
  • 2020-04-11
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多