Store subscribe is not called with ngrx store 4

1.8k views Asked by At

In my angular (4) app I want to introduce a reducer/state management with ngrx 4.

I have a main module

@NgModule({
    imports: [
        // ...
        StoreModule.forRoot({}),
        EffectsModule.forRoot([])
    ],

    declarations: [
        AppComponent
    ],

    bootstrap: [AppComponent]
})

and a lazy loaded module with

@NgModule({
    imports: [
        StoreModule.forFeature('lazy', {
            items: itemsReducer
        }),
        EffectsModule.forFeature([ItemEffects])
    ],

    declarations: [
        // Components & Directives
    ]
})

this is my reducer

export function itemsReducer(state: Item[] = [], action: ItemAction<any>) {
    switch (action.type) {

        case ADD:
            return [action.payload, ...state];

        case DELETE:
            return state.filter((item) => item.id !== action.payload.id);

        case ITEMS_LOADED:
            return Object.assign([], action.payload);

        case LOAD_ITEMS:
            return state;

        default:
            return state;
    }
}

I am also dealing with effects like this:

@Injectable()
export class ItemEffects {

    @Effect() addItem$: Observable<Action> = this.actions$.ofType(ADD)
        .mergeMap((payload: any) =>
            this.dataService.addItem(payload)
                // If successful, dispatch success action with result
                .map((data: any) => {
                    return createLoadItemsAction();
                })
                // If request fails, dispatch failed action
                .catch(() => of({ type: 'FAILED' }))
        );

    @Effect() loadItems$: Observable<Action> = this.actions$.ofType(LOAD_ITEMS)
        .mergeMap(() =>
            this.dataService.getAllItems()
                // If successful, dispatch success action with result
                .map((data: Item[]) => (createItemsLoadedAction(data)))
                // If request fails, dispatch failed action
                .catch(() => of({ type: 'FAILED' }))
        );

    constructor(
        private dataService: DataService,
        private actions$: Actions
    ) { }
}

and in my stateful component I am subscribing to this store like this

export class MainItemsComponent implements OnInit {
    items: Observable<Items[]>;

    constructor(private store: Store<any>) {
        this.items = this.store.select('items');
    }

    ngOnInit() {
        this.store.dispatch(createLoadItemsAction());
    }

    // ...

}

With console.logs I can see that the effects are working, the reducer is called with the correct actions "ITEMS_LOADED" , all the items are inside, but they are not getting passed to my stateful component and are not displayed.

My actions look like this

import { Action } from '@ngrx/store';
import { Item } from '...';

export interface ItemAction<T> extends Action {
    payload?: T;
}

/*
 * action types
 */

export const ADD = 'ADD'
export const DELETE = 'DELETE'
export const LOAD_ITEMS = 'LOAD_ITEMS'
export const ITEMS_LOADED = 'ITEMS_LOADED'

/*
 * action creators
 */

export function createAddItemAction(item: Item): ItemAction<Item> {
    return { type: ADD, payload: item }
}

export function createDeleteItemAction(item: Item): ItemAction<Item> {
    return { type: DELETE, payload: item }
}

export function createItemsLoadedAction(items: Item[]): ItemAction<Item[]> {
    return { type: ITEMS_LOADED, payload: items }
}

export function createLoadItemsAction(): ItemAction<Item> {
    return { type: LOAD_ITEMS }
}

I am using

 "@ngrx/effects": "^4.0.5",
 "@ngrx/store": "^4.0.3",

What am I missing? My goal is to load the items when the component is loaded.

3

There are 3 answers

0
Sharikov Vladislav On

Uhm. If I understand correctly select works another way.

What do you expect now? How this.store.select('items') this should work?

As I understand you need to create selectors. I am not sure that you created them, since I can't see any of them in the code you provided. And also you use select in a strange way.

I think, that you are missing that selectors. You need this:

  • example of creating selector: here
  • example of using selector: here

Or can you please explain what you expect with your current code? :) Maybe I don't know something.

0
A. Moynet On

Try to define the AppState interface and type the state of the store:

interface AppState{
  items: Item[]
}

constructor(private store: Store<AppState>)

Then don't forget to async pipe in the template file (items | async) or subscribe in the component file (But I think you know that)

0
Satpal Tanan On

To get the data you need to use selectors and add a State as mentioned by A. Moynet above

https://ngrx.io/guide/store/selectors

The example by ngrx guys is

import { createSelector } from '@ngrx/store';

export interface FeatureState {
  counter: number;
}

export interface AppState {
  feature: FeatureState;
}

export const selectFeature = (state: AppState) => state.feature;

export const selectFeatureCount = createSelector(
  selectFeature,
  (state: FeatureState) => state.counter
);

And to use this we do this in the component

ngOnInit() {
    this.counter = this.store.pipe(select(fromRoot.selectFeatureCount))
}