代码之家  ›  专栏  ›  技术社区  ›  Armeen Moon

使用featureSelector ngrx时无法读取未定义映射的属性

  •  6
  • Armeen Moon  · 技术社区  · 7 年前

    回购: https://github.com/morningharwood/platform/tree/feature/ngrx-firebase

    当我尝试订阅我的selectAll我的post ngrx实体功能时,我得到了一个错误:

    无法读取未定义映射的属性

    这必须是一个简单的打字错误,我下面的教程一字不差。

    奇怪的是,如果我使用字符串选择器:

     this.selected$ = this.store.select('post');
    
     this.selected$.subscribe(console.log); // no error here.
    

    我得到一个没有错误的答案。

    问题: 使用ngrx实体时,我如何获得 selectAll 选择器从ngrx商店集合注销所有帖子 post ?

    我的 post.reducer.ts

    import {
      PostActions,
      PostActionTypes,
    } from './post.actions';
    
    import {
      ActionReducerMap,
      createFeatureSelector,
    } from '@ngrx/store';
    import {
      createEntityAdapter,
      EntityState,
    } from '@ngrx/entity';
    import { Post } from './post.interfaces';
    
    export const postAdapter = createEntityAdapter<Post>();
    export interface PostState extends EntityState<Post> {}
    
    export const postInitialState: PostState = {
      ids: [ '1234' ],
      entities: {
        '1234': {
          id: '1234',
          header: {
            title: 'title 1',
            subtitle: 'subtitle 1',
          },
          body: {
            sections: [],
          },
        },
      },
    };
    
    export const initialState: PostState = postAdapter.getInitialState(postInitialState);
    
    export function postReducer(state: PostState = initialState, action: PostActions): PostState {
      switch (action.type) {
        case PostActionTypes.ADD_ONE:
          return postAdapter.addOne(action.post, state);
        default:
          return state;
      }
    }
    
    export const getPostState = createFeatureSelector<PostState>('post');
    export const {
      selectIds,
      selectEntities,
      selectAll: selectAllPosts,
      selectTotal,
    } = postAdapter.getSelectors(getPostState);
    
    export const postReducersMap: ActionReducerMap<any> = {
      post: postReducer,
    };
    

    app.component :

    import {
      Component,
      OnInit,
    } from '@angular/core';
    import { AppState } from './app.interfaces';
    import { Store } from '@ngrx/store';
    import { selectAllPosts } from './post/post.reducer';
    
    import { Observable } from 'rxjs/Observable';
    
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ],
    })
    export class AppComponent implements OnInit {
      public selected$: Observable<any>;
    
      constructor(private store: Store<AppState>) {
      }
    
      ngOnInit() {
    
        this.selected$ = this.store.select(selectAllPosts);
        this.selected$.subscribe(console.log); // error here
      }
    }
    

    我的 forFeature forRoot 模块:

    @NgModule({
      imports: [
        BrowserModule.withServerTransition({ appId: 'portfolio-app' }),
        StoreModule.forRoot({ reducers }),
        EffectsModule.forRoot([]),
        PostModule,
      ],
      declarations: [ AppComponent ],
      bootstrap: [ AppComponent ],
      providers: [],
    })
    export class AppModule {
    }
    
    import { StoreModule } from '@ngrx/store';
    import { postReducers } from './post.reducer';
    
    
    @NgModule({
      exports: [],
      declarations: [],
      imports: [StoreModule.forFeature('post', postReducersMap)],
    })
    export class PostModule {
    }
    
    4 回复  |  直到 7 年前
        1
  •  13
  •   Jadams    6 年前

    根据此处的(过帐时有些不完整)文档, ngrx.io/entity/adapter ,您需要使用 createFeatureSelector 函数,然后使用该子状态和适配器选择器来选择该状态的切片。

    更改此项:

    post.reducer.ts

    export const getPostState = createFeatureSelector<PostState>('post');
    export const {
      selectIds,
      selectEntities,
      selectAll: selectAllPosts,
      selectTotal,
    } = postAdapter.getSelectors(getPostState);
    

    为此:

    export const selectPostState = createFeatureSelector<PostState>('post');
    export const {
      selectIds,
      selectEntities,
      selectAll
      selectTotal,
    } = postAdapter.getSelectors();
    
    export const selectPostIds = createSelector(
      selectPostState,
      selectIds
     );
    
    export const selectPostEntities = createSelector(
      selectPostState,
      selectEntities
    );
    
    export const selectAllPosts = createSelector(
      selectPostState,
      selectAll
    );
    

    这将选择 postState 特色

    希望这有帮助

    编辑:看来上面两个例子是等价的,我面临的问题与 Matthew Harwood 这是对 ActionReducerMap .

    我认为文档在这方面可能有点混乱,因为他们认为和实体的状态与功能状态(例如延迟加载状态)的状态是相同的。

    虽然实体状态具有id和实体等属性,但它们不需要每个特征状态都具有的reduce,而只需要一个reducer来覆盖该模型的所有实体状态。

    例如

    export const initialState: PostState = postAdapter.getInitialState(postInitialState);
    

    不需要减速器映射 PostAdapterState 因为一个减速器

    export function postReducer(state: PostState = initialState, action: 
    PostActions): PostState {
      switch (action.type) {
        case PostActionTypes.ADD_ONE:
          return postAdapter.addOne(action.post, state);
        default:
          return state;
      }
    }
    

    涵盖所有实体状态更新。

    如果在功能状态(如下面的示例)中有多个适配器状态,则需要减速器映射。

    假设你有一个图书馆功能状态,以及书籍和杂志的实体。它看起来像下面这样。

    book.reducer.ts

    export interface BookEntityState extends EntityState<Book> {
      selectedBookId: string | null;
    }
    
    const bookAdapter: EntityAdapter<Book> = createEntityAdapter<Book>();
    
    export const initialBookState: BookEntityState = adapter.getInitialState({
      selectedBookId: null
    });
    
    export const bookReducer (...) ... etc
    

    magazine.reducer.ts

    export interface MagazineEntityState extends EntityState<Magazine> {
      selectedMagazineId: string | null;
    }
    
    const magazineAdapter: EntityAdapter<Magazine> = createEntityAdapter<Magazine>();
    
    export const initialMagazineState: MagazineEntityState = 
    adapter.getInitialState({
      selectedMagazineId: null
    });
    
    export const magazineReducer (...) ... etc
    

    library.reducer.ts

    // Feature state,
    export interface LibraryFeatureState {
      books: BookEntityState
      magazines: MagazineEntityState
    }
    
    // Reducer map of the lirbray
    export const libraryReducersMap: ActionReducerMap<LibraryFeatureState> = {
      books: bookReducer,
      magazines: magazineReducer
    };
    

    然后将功能状态添加到LibraryModule imports阵列中的存储中。

    library.module.ts

    ...
    StoreModule.forFeature<LibraryFeatureState>('libraryState', 
    libraryReducersMap),
    ...
    

    希望这对其他人有帮助。

        2
  •  3
  •   Romaric    6 年前

    我在创建选择器时遇到了相同的问题,如下所示

    export const selectFuturMenus = createSelector(
      selectMenuState,
      fromMenu.selectAll,
      (state, menus: Menu[], props) => menus.filter(menu => {
        let startMmt = moment(menu.start_date, 'YYYY-MM-DD');
        let endMmt = moment(menu.end_date, 'YYYY-MM-DD');
        let mmt = moment();
        console.log(startMmt, endMmt);
        return mmt.isBefore(endMmt);
      })
    );
    

    似乎我们不能在筛选函数之前使用适配器选择器。 遵循NgRX文档,一个简单的解决方案是像这样分解选择器

    export const selectAllMenus = createSelector(
      selectMenuState,
      fromMenu.selectAll,
    );
    
    export const selectFuturMenus = createSelector(
      selectAllMenus,
      (menus: Menu[], props) => menus.filter(menu => {
        let startMmt = moment(menu.start_date, 'YYYY-MM-DD');
        let endMmt = moment(menu.end_date, 'YYYY-MM-DD');
        let mmt = moment();
        return mmt.isBefore(endMmt);
      })
    );
    
        3
  •  1
  •   Juan    5 年前

    我在几个小时内一直在思考同一个问题,最终我意识到问题的根源与reduced函数有关,具体来说,我的reducer函数没有在switch语句末尾返回默认状态。

    正如我所说,我花了一段时间才找到答案,我希望我的回答能帮助任何人比我更快地解决这个问题。

    switch (action.type) {
    
        case ActionType:
          return adapter.addAll(action.payload, state);
    
        default:
          return state;
      }
    
        4
  •  0
  •   Armeen Moon    7 年前

    它是 ActionMapReducer forFeature('post', postReducerMap); 我仍然需要弄清楚如何同时使用实体和ActionMapReducer。