0
votes

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
update the post with the ItemAction class fileAravind
how are you testing the fact that this.items = this.store.select('items') is not working? If you could show your template as the error could be in thereMeeker
its pretty hard to see the problem like this it would be better if you could replicate a small plunker . for more on ngrx check this linkRahul Singh

3 Answers

1
votes

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.

1
votes

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))
}
0
votes

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)