I'm trying to implement a NgRx store using @ngrx/entity lib, but I can't retrieve any data by using @ngrx/entity getSelectors. Redux Devtools shows my collection loaded by Effect()'s as entities properly. So my Effect is working properly. Now I want to select the data as array by using the selectAll
selector from adapter.getSelectors()
function. My reducer index looks as follows
reducers/index.ts
import { ActionReducerMap, createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromModels from './models.reducer';
import { EntityState } from '@ngrx/entity';
export interface State {
models: fromModels.State;
}
export const reducers: ActionReducerMap<State> = {
models: fromModels.reducer
};
export const getModelsState = createFeatureSelector<fromModels.State>('models');
export const { selectAll: getAllModels } = fromModels.adapter.getSelectors(getModelsState);
reducers/models.reducer.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { createEntityAdapter, EntityState, EntityAdapter } from '@ngrx/entity';
import { ModelsActions, ModelsActionTypes } from '../actions';
import { Model } from '../../models/model';
export const adapter: EntityAdapter<Model> = createEntityAdapter<Model>({
selectId: model => model.id,
sortComparer: (modelA, modelB) => modelA.id === modelB.id ? 0 : (modelA.id === modelB.id ? 1 : -1)
});
export interface State extends EntityState<Model> {}
const initialState = adapter.getInitialState();
export function reducer(state = initialState, action: ModelsActions): State {
switch (action.type) {
case ModelsActionTypes.LoadSuccess:
return adapter.addAll(action.payload, state);
default: return state;
}
}
In my container component I want to select the data by the ngrx/entity selector and display the data using the async pipe. (I reduced the template)
models.component.ts
// All other import statements
import { Store, select } from '@ngrx/store';
import * as fromFeature from '../store';
@Component({
template: `{{models$ | async}} | json`
})
export class ModelsComponent implements OnInit {
models$: Observable<Model[]>;
constructor(private store: Store<fromFeature.State>) {}
ngOnInit() {
this.models$ = this.store.select(fromFeature.getAllModels);
this.store.dispatch(new fromFeature.LoadModels());
}
}
The console prints an error
ModelsComponent.html:2
ERROR TypeError: Cannot read property 'ids' of undefined
at selectIds (entity.es5.js:45)
at eval (store.es5.js:572)
Any suggestions or ideas solving this? Thanks!
UPDATE [Requested template]
The template just subscribes to the models$
observable, same as in the reduces template. The model-card.component.ts just gets Input()'s and ng-content.
<app-model-card
*ngFor="let model of models$ | async"
[logo]="model.id"
[routerLink]="[model.id]">
{{model.title}}
</app-model-card>
UPDATE [actions requested]
actions/models.action.ts
import { Action } from '@ngrx/store';
import { Model } from '../../models/model';
export enum ModelsActionTypes {
Load = '[Models] Load',
LoadSuccess = '[Models] Load Success',
LoadFailed = '[Models] Load Failed'
}
export class LoadModels implements Action {
readonly type = ModelsActionTypes.Load;
}
export class LoadModelsSuccess implements Action {
readonly type = ModelsActionTypes.LoadSuccess;
constructor(public payload: Model[]) {}
}
export class LoadModelsFailed implements Action {
readonly type = ModelsActionTypes.LoadFailed;
constructor(public payload: any) {}
}
export type ModelsActions =
LoadModels |
LoadModelsSuccess |
LoadModelsFailed;
models.effects.ts
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map, switchMap, catchError } from 'rxjs/operators';
import { ModelsActionTypes, LoadModelsSuccess, LoadModelsFailed } from
'../actions';
import { ModelsService } from '../../services/models.service';
@Injectable()
export class ModelsEffects {
constructor(
private actions$: Actions,
private modelsService: ModelsService
) {}
@Effect()
loadModels$ = this.actions$
.ofType(ModelsActionTypes.Load)
.pipe(
switchMap(() => {
return this.modelsService.getModels().pipe(
map(models => new LoadModelsSuccess(models)),
catchError(error => of(new LoadModelsFailed(error)))
);
})
);
}
this.models$ = this.store.select(fromFeature.getAllModels)
->this.models$ = this.store.select(fromFeature.selectAll)
? – Richard Matsen'./models.reducer'
butreducers/model.reducers.ts
- but that's got to be a typo in the question, right? – Richard Matsen