REPO:https://github.com/morningharwood/platform/tree/feature/ngrx-firebase
当我尝试订阅我的postAll的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 {
}
答案 0 :(得分:4)
对于将来遇到这种情况的人,可以通过稍微重构选择器来解决。
根据此处ngrx.io/entity/adapter(在发布时有些不完整)的文档,您需要使用createFeatureSelector
函数选择featureSate,然后将该子状态与适配器选择器一起使用以选择切片那个状态。
更改此:
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和实体等属性,但是它们不需要像每个相似特征状态那样的化简,而仅需要一个化简器来覆盖该模型的所有实体状态。
例如
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导入数组中的商店中。
library.module.ts
...
StoreModule.forFeature<LibraryFeatureState>('libraryState',
libraryReducersMap),
...
希望这对其他人有帮助。
答案 1 :(得分:1)
我尝试使用以下内容创建选择器时遇到了相同的问题
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);
})
);
答案 2 :(得分:0)
在ActionMapReducer
中创建了forFeature('post', postReducerMap);
我还需要弄清楚如何一起使用实体和ActionMapReducer。
答案 3 :(得分:0)
几个小时以来,我一直在为同一个问题苦苦思索,最后我意识到问题的根源与reduce函数有关,具体地说,我的reducer函数并没有在还原结束时返回默认状态切换语句。
正如我所说,花了一段时间才找到答案,希望我的回答能帮助其他人比我更快地解决此问题。
switch (action.type) {
case ActionType:
return adapter.addAll(action.payload, state);
default:
return state;
}