> For the complete documentation index, see [llms.txt](https://duncanhunter.gitbook.io/angular-and-ngrx/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://duncanhunter.gitbook.io/angular-and-ngrx/23.-add-guests-logic.md).

# 23. Add attendee logic

\
1\. Add new action creators
---------------------------

* Add the add ability to our feature state action creators.

{% code title="src/app/event/state/attendees/attendees.actions.ts" %}

```typescript
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',
  AddAttendee = '[Attendee Page] Add Attendee',
  AddAttendeeSuccess = '[Attendee API] Add Attendee Success',
  AddAttendeeFail = '[Attendee API] Add Attendee 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 class AddAttendee implements Action {
  readonly type = AttendeesActionTypes.AddAttendee;
  constructor(public payload: Attendee) {}
}
export class AddAttendeeSuccess implements Action {
  readonly type = AttendeesActionTypes.AddAttendeeSuccess;
  constructor(public payload: Attendee) {}
}
export class AddAttendeeFail implements Action {
  readonly type = AttendeesActionTypes.AddAttendeeFail;
  constructor(public payload: any) {}
}
export type AttendeesActions =
  | AddAttendee
  | AddAttendeeSuccess
  | AddAttendeeFail
  | LoadAttendees
  | LoadAttendeesSuccess
  | LoadAttendeesFail;
```

{% endcode %}

## 2. Add effect

* add effect

{% code title="src/app/event/state/attendees/attendees.effects.ts" %}

```typescript
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { ofType } from '@ngrx/effects';
import { switchMap, map, catchError } from 'rxjs/operators';
import { of } from 'rxjs';

import { EventService } from '../../services/event.service';
import {
  AttendeesActionTypes,
  LoadAttendees,
  LoadAttendeesSuccess,
  LoadAttendeesFail,
  AddAttendee,
  AddAttendeeSuccess,
  AddAttendeeFail
} from './attendees.actions';
import { Attendee } from '../../../models';

@Injectable()
export class AttendeesEffects {
  constructor(private actions$: Actions, private eventService: EventService) {}

  @Effect()
  getAttendees$ = this.actions$.pipe(
    ofType(AttendeesActionTypes.LoadAttendees),
    switchMap((action: LoadAttendees) =>
      this.eventService.getAttendees().pipe(
        map((attendees: Attendee[]) => new LoadAttendeesSuccess(attendees)),
        catchError(error => of(new LoadAttendeesFail(error)))
      )
    )
  );

  @Effect()
  addAttendee$ = this.actions$.pipe(
    ofType(AttendeesActionTypes.AddAttendee),
    switchMap((action: AddAttendee) =>
      this.eventService.addAttendee(action.payload).pipe(
        map((attendee: Attendee) => new AddAttendeeSuccess(attendee)),
        catchError(error => of(new AddAttendeeFail(error)))
      )
    )
  );
}
```

{% endcode %}

## 3. Update reducer

* Update reducer to have a case for adding one attendee to the store using the `adapter.addOne` method.

{% code title="src/app/event/state/attendees/attendees.reducer.ts" %}

```typescript
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';

import { AttendeesActions, AttendeesActionTypes } from './attendees.actions';
import { Attendee } from '../../../models';

export interface State extends EntityState<Attendee> {
  loading: boolean;
  error: any;
}

const adapter: EntityAdapter<Attendee> = createEntityAdapter<Attendee>();

export const intitalState: State = adapter.getInitialState({
  loading: false,
  error: null
});

export function reducer(state = intitalState, action: AttendeesActions): State {
  switch (action.type) {
    case AttendeesActionTypes.LoadAttendees: {
      return adapter.removeAll({
        ...state,
        loading: false,
        error: null
      });
    }

    case AttendeesActionTypes.LoadAttendeesSuccess: {
      return adapter.addAll(action.payload, {
        ...state,
        loading: false,
        error: null
      });
    }

    case AttendeesActionTypes.LoadAttendeesFail: {
      return adapter.removeAll({
        ...state,
        loading: false,
        error: action.payload
      });
    }

    case AttendeesActionTypes.AddAttendeeSuccess: {
      return adapter.addOne(action.payload, { ...state, error: null });
    }

    case AttendeesActionTypes.AddAttendeeFail: {
      return { ...state, error: action.payload };
    }

    default: {
      return state;
    }
  }
}

export const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal
} = adapter.getSelectors();
```

{% endcode %}

## 4. Update EventComponent to dispatch AddAttendees

* Update component and remove spinner, we can now use loading state property for the attendees slice of state.

{% code title="src/app/event/containers/event/event.component.ts" %}

```typescript
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Store, select } from '@ngrx/store';

import { Attendee } from '../../../models';
import { EventService } from '../../services/event.service';
import {
  StartSpinner,
  StopSpinner
} from '../../../state/spinner/spinner.actions';
import { getSpinner } from '../../../state/spinner/spinner.selectors';
import {
  LoadAttendees,
  AddAttendee
} from '../../state/attendees/attendees.actions';
import { State } from '../../state';
import { getAttendees } from '../../state/attendees/attendees.selectors';

@Component({
  selector: 'app-event',
  templateUrl: './event.component.html',
  styleUrls: ['./event.component.scss']
})
export class EventComponent implements OnInit {
  spinner$: Observable<boolean>;
  attendees$: Observable<Attendee[]>;

  constructor(
    private store: Store<State>,
    private eventService: EventService
  ) {}

  ngOnInit() {
    this.attendees$ = this.store.pipe(select(getAttendees));
    this.store.dispatch(new LoadAttendees());
  }

  addAttendee(attendee: Attendee) {
    this.store.dispatch(new AddAttendee(attendee));
  }
}
```

{% endcode %}

## StackBlitz Link

{% embed url="<https://stackblitz.com/github/duncanhunter/angular-and-ngrx-demo-app/tree/23-add-attendee-logic>" %}
Web Link: Link to the demo app running in StackBlitz
{% endembed %}
