19. Create feature state

In this section we will make a new piece of feature state which will build on our knowledge from implementing the spinner state.

1. Add state folder and attendee actions

The best place to start when adding a new slice of state and the associated reducer, effects, selectors and tests is with the action creators. Starting with action creators helps be clear about what you want this section to be able to do and like tests becomes self documenting.

  • Create a state folder and a attendees sub-folder.

  • Create a attendees.actions.ts file.

  • Add the following actions to load attendees.

src/app/event/state/attendees/attendees.actions.ts
import { Action } from '@ngrx/store';
import { Attendee } from '../../../models';

export enum AttendeesActionTypes {
  LoadAttendees = '[Attendees Page] Load Attendees',
  LoadAttendeesSuccess = '[Attendees Page] Load Attendees Success',
  LoadAttendeesFail = '[Attendees Page] Load Attendees Fail'
}

export class LoadAttendees implements Action {
  readonly type = AttendeesActionTypes.LoadAttendees;
}

export class LoadAttendeesSuccess implements Action {
  readonly type = AttendeesActionTypes.LoadAttendeesSuccess;
  constructor(public payload: Attendee[]) {}
}

export class LoadAttendeesFail implements Action {
  readonly type = AttendeesActionTypes.LoadAttendeesFail;
  constructor(public payload: any) {}
}

export type AttendeesActions =
  | LoadAttendees
  | LoadAttendeesSuccess
  | LoadAttendeesFail;

2. Create a attendees reducer

  • Create a attendees.reducer.ts file in the state/attendees folder.

  • Add a interface called State describing this slice of state.

  • Add an initial state that implements this new State interface.

  • Add a reducer function that uses our initialState, AttendeesActionTypes and AttendeesActions types. We will come back and talk about this when we do effects in the next section.

src/app/event/state/attendees/attendees.reducer.ts
import { Attendee } from '../../../models';
import { AttendeesActions, AttendeesActionTypes } from './attendees.actions';

export interface State {
  attendees: Attendee[];
  loading: boolean;
}

export const intitalState: State = {
  attendees: [],
  loading: false
};

export function reducer(state = intitalState, action: AttendeesActions): State {
  switch (action.type) {
    case AttendeesActionTypes.LoadAttendees:
      {
      }

      break;

    default: {
      return state;
    }
  }
}

3. Add index.ts file to expose state logic from feature module

Index.ts files are like a public API for our feature state that re-exports everything we want to expose to our parts of our Angular application. Having an index.ts file helps other developers know what they should be able to access from this module.

In this index.ts file we will extend the feature State interface with our global State interface allowing us to have a more complete interface. We can not know what other lazy loaded state will have been loaded so this is not on the interface.

  • Create a index.ts file inside the event/state folder.

  • Import our global state name spaced to fromRoot.

  • Import our attendee state name spaced as fromAttendees.

  • Make a new EventState object that extends the root state.

  • Make a ActionReducerMap that is a Map of all the reducers in this feature of which we have only one.

src/app/event/state/index.ts
import { ActionReducerMap } from '@ngrx/store';

import * as fromRoot from './../../state/state';
import * as fromAttendees from './attendees/attendees.reducer';

export interface EventState {
  attendees: fromAttendees.State;
}

export interface State extends fromRoot.State {
  event: EventState;
}

export const reducers: ActionReducerMap<EventState> = {
  attendees: fromAttendees.reducer
};

4. Register feature state in the EventModule

  • Register StoreModule.forFeature in the EventModule and pass in our reducer map from the index.ts file.

  • We need to name each piece of state even if it is a map of one or more reducers. This means our state tree will start with properties named after each feature state slice and one for each root state slice like below.

{
    spinner: {...}
    event: {...}
    someOtherFeatureState: {...}
}
src/app/event/event.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';

import { EventComponent } from './containers/event/event.component';
import { AddAttendeeComponent } from './components/add-attendee/add-attendee.component';
import { EventListComponent } from './components/event-list/event-list.component';
import { StoreModule } from '@ngrx/store';
import { reducers } from './state';
@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: '', component: EventComponent }]),
    ReactiveFormsModule,
    HttpClientModule,
    StoreModule.forFeature('event', reducers)
  ],
  declarations: [EventComponent, AddAttendeeComponent, EventListComponent]
})
export class EventModule {}

5. Examine the state tree in the devtools

  • Open browser and go to event page

Last updated