# 13. Add simple NgRx spinner

## [Link to section slides](https://docs.google.com/presentation/d/1Y7Tf7kjO4Li0ihhkVgRjn4szFJPAkbMvilfrDCbrjq8/edit#slide=id.g2fa7fd70ec_0_1818)

## 1. npm i NgRx Store library

* npm install @ngrx/store

```
npm i @ngrx/store
```

## 2.  Add a reducer

Reducers are at the core of the redux pattern NgRx follows. We will have a reducer for each "slice" of state that we will combine into a single "store" that is literally just a JavaScript object.&#x20;

This is a very simple reducer, but it follows the basic principles of a reducer. Reducers are just pure functions that take in state and a action (the instructions to change state) and return the new state for this slice of state.

* Create a state folder with a spinner folder inside of it.
* Create a spinner.reducer.ts file and add the below spinner state logic.

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

```typescript
export function reducer(state = { isOn: false }, action) {
  switch (action.type) {
    case 'startSpinner': {
      return {
        isOn: true
      };
    }

    case 'stopSpinner': {
      return {
        isOn: false
      };
    }

    default:
      return state;
  }
}

```

{% endcode %}

## 3. Register NgRx in app module

Here we register the reducer we made and name this slice of state "spinner". You will see this piece of state in the dev tools under a property called "spinner", when we add the dev tools in the coming sections.

* Add NgRx to AppModule

{% code title="src/app/app.module.ts" %}

```typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/containers/home/home.component';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { StoreModule } from '@ngrx/store';

import { InMemoryDataService } from './app.db';
import { reducer } from './state/spinner/spinner.reducer';

@NgModule({
  declarations: [AppComponent, HomeComponent],
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      { path: '', pathMatch: 'full', redirectTo: 'home' },
      { path: 'home', component: HomeComponent },
      { path: 'event', loadChildren: './event/event.module#EventModule' }
    ]),
    HttpClientModule,
    HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { delay: 1000 }),
    StoreModule.forRoot({ spinner: reducer })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

```

{% endcode %}

## 4. Inject store into the EventComponent

As we move towards fully implementing NgRx you will see our components become even simpler with just a bunch of subscriptions to the store and actions being dispatched and work normally done in components now delegated to our NgRx system.

* Inject Store into the EventComponent.
* Select the `spinner$` state.

{% code title="src/app/event/container/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';

@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<any>, private eventService: EventService) {}

  ngOnInit() {
    this.getAttendees();
    this.spinner$ = this.store.pipe(select(state => state.spinner.isOn));
  }

  getAttendees() {
    this.attendees$ = this.eventService.getAttendees();
  }

  addAttendee(attendee: Attendee) {
    this.eventService.addAttendee(attendee).subscribe(() => {
      this.getAttendees();
    });
  }
}

```

{% endcode %}

## 5. Dispatch an action when adding new attendees

* Dispatch a `startSpinner` and `stopSpinner` action when loading and receiving data from the fake backend.

{% code title="src/app/event/container/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';

@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<any>, private eventService: EventService) {}

  ngOnInit() {
    this.getAttendees();
    this.spinner$ = this.store.pipe(select(state => state.spinner.isOn));
  }

  getAttendees() {
    this.attendees$ = this.eventService.getAttendees();
  }

  addAttendee(attendee: Attendee) {
    this.store.dispatch({ type: 'startSpinner' });
    this.eventService.addAttendee(attendee).subscribe(() => {
      this.store.dispatch({ type: 'stopSpinner' });
      this.getAttendees();
    });
  }
}

```

{% endcode %}

## 6. Update the EventComponent to show a basic loading indicator

* Add a loading div with an ngIf.
* Add a `*ngIf` to the `EventListComponent`.

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

```markup
<app-add-attendee (addAttendee)="addAttendee($event)"></app-add-attendee>
<app-event-list *ngIf="!(spinner$ | async)" [attendees]="attendees$ | async"></app-event-list>
<div *ngIf="spinner$ | async">loading..</div>

```

{% endcode %}

![Image: Showing 'spinner' state registered in AppModule and used in the component.](https://3531511677-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LLqc06ABPywqowC_y8E%2F-LM7Y_uFoB4MdSc05uH9%2F-LM7hbJI2t-w93q23pb-%2Fimage.png?alt=media\&token=3dcb69f5-f832-4a3d-b25a-907e776515bd)

## StackBlitz Link

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