Reputation: 43
I have a problem with Ngrx in my Ionic app.
My store is composed of two types of objects "Compteur" and "Prise[]".
I used the ngrx entities for "Prise" objects and that works perfectly fine.
But with Compteur object, initial state seems to be set undifined and so, my selectors make the app crash.
If I don't try to subscribe, store builds perfectly.
How can I fix this?
How does it work?
I have this error:
Runtime Error
Cannot read property 'compteur' of undefined
Stack
TypeError: Cannot read property 'compteur' of undefined
at http://localhost:8100/build/main.js:100:145
at http://localhost:8100/build/vendor.js:27678:30
at memoized (http://localhost:8100/build/vendor.js:27615:28)
at defaultStateFn (http://localhost:8100/build/vendor.js:27649:39)
at http://localhost:8100/build/vendor.js:27681:36
at MapSubscriber.memoized [as project] (http://localhost:8100/build/vendor.js:27615:28)
at MapSubscriber._next (http://localhost:8100/build/vendor.js:56535:35)
at MapSubscriber.Subscriber.next (http://localhost:8100/build/vendor.js:22085:18)
at MapSubscriber._next (http://localhost:8100/build/vendor.js:56541:26)
at MapSubscriber.Subscriber.next (http://localhost:8100/build/vendor.js:22085:18)
in my app.component.ts where I call selectors:
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = HomePage;
compteur$: Observable<Compteur>;
prises$: Observable<Prise[]>;
pages: Array<{ title: string, component: any }>;
constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen, public store: Store<AppState>) {
this.initializeApp();
// used for an example of ngFor and navigation
this.pages = [
{title: 'Accueil', component: HomePage},
{title: 'Configuration', component: ListPage}
];
this.compteur$ = this.store.select(fromCompteur.getCompteur);
this.compteur$.subscribe(()=> {
console.log("NOT WORKING");
});
this.prises$ = this.store.select(fromPrise.getPrise);
this.prises$.subscribe(()=>{
console.log("WORKING");
});
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
this.store.dispatch(new fromCompteurActions.LoadAction());
this.store.dispatch(new fromPriseActions.LoadAction());
});
}
openPage(page) {
this.nav.setRoot(page.component);
}
}
compteur.actions.ts:
export const LOAD_COMPTEUR = "[COMPTEUR] LOAD";
export const LOAD_COMPTEUR_COMPLETED = "[COMPTEUR] LOAD COMPLETED";
export const UPDATE_COMPTEUR = "[COMPTEUR] UPDATE";
export class LoadAction implements Action {
readonly type = LOAD_COMPTEUR;
constructor(){};
}
export class LoadCompletedAction implements Action {
readonly type = LOAD_COMPTEUR_COMPLETED;
constructor(public payload: {compteur: Compteur}){};
}
export class UpdateAction implements Action {
readonly type = UPDATE_COMPTEUR;
constructor(public payload: {compteur: Compteur}){};
}
export type compteurActionType =
| LoadAction
| LoadCompletedAction
| UpdateAction
;
compteur.reducer.ts
const initialState : CompteurState = {
compteur: null,
loading: false
};
export function reducer(state = initialState,
action: fromCompteur.compteurActionType): CompteurState{
switch (action.type){
case fromCompteur.UPDATE_COMPTEUR: {
return {
compteur: action.payload.compteur,
loading: false
};
}
case fromCompteur.LOAD_COMPTEUR: {
return {...state,
loading: true
};
}
case fromCompteur.LOAD_COMPTEUR_COMPLETED: {
return {
compteur: action.payload.compteur,
loading: false
};
}
default: {
return state;
}
}
}
export const getCompteurState = createFeatureSelector<CompteurState>('compteur');
export const getCompteur = createSelector(getCompteurState, (state: CompteurState) => state.compteur);
export const getLoading = createSelector(getCompteurState, (state: CompteurState) => state.loading);
reducer/index.ts
export const reducers: ActionReducerMap<AppState> = {
priseState: priseReducer.reducer,
compteurState: compteurReducer.reducer
};
export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
return function(state: AppState, action: any): AppState {
console.log('state', state);
console.log('action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<AppState>[] = [logger];
compteur.effects.ts
@Injectable()
export class CompteurEffects {
constructor(private actions$: Actions,
private dataService : DataService){};
@Effect()
loadCompteur$: Observable<Action> = this.actions$
.ofType(fromCompteur.LOAD_COMPTEUR)
.switchMap(()=>
this.dataService.getCompteur()
.map(data => new fromCompteur.LoadCompletedAction({ compteur: data })
)
);
}
app.state.ts
export interface AppState {
priseState: PriseState;
compteurState: CompteurState;
}
export interface PriseState extends EntityState<Prise>{
selectedPriseId: string | number | null,
loading: boolean
}
export interface CompteurState {
compteur: Compteur,
loading: boolean
}
package.json
"dependencies": {
"@angular/common": "5.0.3",
"@angular/compiler": "5.0.3",
"@angular/compiler-cli": "5.0.3",
"@angular/core": "5.0.3",
"@angular/forms": "5.0.3",
"@angular/http": "5.0.3",
"@angular/platform-browser": "5.0.3",
"@angular/platform-browser-dynamic": "5.0.3",
"@ionic-native/core": "4.4.0",
"@ionic-native/splash-screen": "4.4.0",
"@ionic-native/status-bar": "4.4.0",
"@ionic/storage": "2.1.3",
"@ngrx/core": "^1.2.0",
"@ngrx/effects": "^5.2.0",
"@ngrx/entity": "^5.2.0",
"@ngrx/store": "^5.2.0",
"@ngrx/store-devtools": "^5.2.0",
"chart.js": "^2.7.2",
"ionic-angular": "3.9.2",
"ionicons": "3.0.0",
"ng2-charts": "^1.6.0",
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"
},
"devDependencies": {
"@ionic/app-scripts": "3.1.8",
"typescript": "2.4.2"
}
Upvotes: 1
Views: 689
Reputation: 43
Ok I found my error:
I made a mistake in compteur.reducer.ts
Just need to replace this:
export const getCompteurState = createFeatureSelector<CompteurState>('compteur');
by
export const getCompteurState = createFeatureSelector<CompteurState>('compteurState');
Upvotes: 1