11. Create EventService In this section we will add a service and discuss dependency injection.
1. Add an Angular service and return an observable of Attendee
Run the following command to make a service.
Copy ng g service event/services/event
2. Add logic to the service
Add logic to the EventService
to return a hardcoded Attendee
observable array.
src/app/event/services/event.service.ts
Copy import { Injectable } from '@angular/core' ;
import { Attendee } from '../../models' ;
import { Observable , of } from 'rxjs' ;
@ Injectable ({
providedIn : 'root'
})
export class EventService {
constructor () {}
getAttendees () : Observable < Attendee []> {
return of ([
{
name : 'Duncan' ,
attending : true ,
guests : 0
}
] as Attendee []);
}
}
3. Inject service into the component
Inject and use the new service in the component.
Subscribe to the observable of Attendee returned from the service.
Add a getAttendees method to the component.
src/app/event/containers/event/event.component.ts
Copy import { Component , OnInit } from '@angular/core' ;
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 {
attendees : Attendee [] = [];
constructor ( private eventService : EventService ) {}
ngOnInit () {
this .getAttendees ();
}
getAttendees () {
this .eventService
.getAttendees ()
.subscribe (attendees => ( this .attendees = attendees));
}
addAttendee (attendee : Attendee ) {
this .attendees = [ ... this .attendees , attendee];
console .log (
'TCL: EventComponent -> addAttendee -> this.attendees' ,
this .attendees
);
}
}
4. Swap from subscription to async pipe
Angular pipes , a way to write display-value transformations that you can declare in your HTML. The async pipe is a special built in pipe from Angular that will subscribe to an observable for you in the HTML template and also unsubscribe when the components ngOnDestory
life cycle hook is fired. Another quirk is that it will also mark the component to be checked by Angular's change detection on its next cycle.
Use async pipe versus a subscription to get the attendees from the observable.
src/app/event/containers/event/event.component.ts
Copy import { Component , OnInit } from '@angular/core' ;
import { Attendee } from '../../../models' ;
import { EventService } from '../../services/event.service' ;
import { Observable } from 'rxjs' ;
@ Component ({
selector : 'app-event' ,
templateUrl : './event.component.html' ,
styleUrls : [ './event.component.scss' ]
})
export class EventComponent implements OnInit {
attendees$ : Observable < Attendee []>;
constructor ( private eventService : EventService ) {}
ngOnInit () {
this .getAttendees ();
}
getAttendees () {
this .attendees$ = this . eventService .getAttendees ();
}
}
5. Use async pipe in HTML
Swap out attendee for attendee$ | async
in the HTML. We can leave the original attendees property alone for now.
src/app/event/containers/event/event.component.html
Copy <app-add-attendee (addAttendee)="addAttendee($event)"></app-add-attendee>
<app-event-list [attendees]="attendees$ | async"></app-event-list>
6. Add fake backend
Angular's own HttpClientInMemoryWebApiModule
can help us make a little mock server without needing to add a real server set up. You can read more about how it works here https://github.com/angular/in-memory-web-api .
Add fake backend with with npm.
Copy npm i angular-in-memory-web-api -D
Add the HttpClientInMemoryWebApiModule
to the imports array of the @ngModule
and register the InMemoryDataService
we will write in the next step.
Copy 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 { InMemoryDataService } from './app.db' ;
@ 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 : 100 }) ,
] ,
providers : [] ,
bootstrap : [AppComponent]
})
export class AppModule {}
7. Make the InMemoryDataService
Create the missing InMemoryDataService
in the src/app folder. Do not worry too much about learning how this works we just need a little fake backend so we can focus on understanding Angular and NgRx.
Copy import { InMemoryDbService } from 'angular-in-memory-web-api' ;
import { Attendee } from './models' ;
export class InMemoryDataService implements InMemoryDbService {
createDb () {
const attendees = [
{
id : 1 ,
name : 'Duncan In Memory' ,
attending : true ,
guests : 0
}
] as Attendee [];
return { attendees };
}
}
8. Update Attendee interface to have an optional Id
Add optional id to Attendee interface with a id?: number
syntax.
src/app/models/attendee.ts
Copy export interface Attendee {
id ?: number ;
name : string ;
attending : boolean ;
guests : number ;
}
9. Add HttpClientModule to EventModule
Angular modules describe the dependencies for this section of code so we will need to also add the HttpClientModule here to. The build tool is smart enough to know we registered it in the root module so we will not pay for it twice in the browser bundled JavaScript.
Add HttpClientModule
to the EventModule.
src/app/event/event.module.ts
Copy 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' ;
@ NgModule ({
imports : [
CommonModule ,
RouterModule .forChild ([{ path : '' , component : EventComponent }]) ,
ReactiveFormsModule ,
HttpClientModule
] ,
declarations : [EventComponent , AddAttendeeComponent , EventListComponent]
})
export class EventModule {}
10. Update service to call fake endpoint
Update the service by injecting the httpClient
into the constructor.
Change the getAttendee
method to use the httpClient
and fake backend.
Add an addAttendee
method to save the added attendees.
src/app/event/event.service.ts
Copy import { Injectable } from '@angular/core' ;
import { Attendee } from '../../models' ;
import { Observable , of } from 'rxjs' ;
import { HttpClient } from '@angular/common/http' ;
@ Injectable ({
providedIn : 'root'
})
export class EventService {
constructor ( private httpClient : HttpClient ) {}
getAttendees () : Observable < Attendee []> {
return this . httpClient .get < Attendee []>( '/api/attendees' );
}
addAttendee (attendee : Attendee ) : Observable < Attendee > {
return this . httpClient .post < Attendee >( '/api/attendees' , attendee);
}
}
12. Update container component to have an add attendee method
Add new method to call addAttendee
on the service and then call getAttendees
after it saves.
src\app\event\containers\event\event.component.ts
Copy import { Component , OnInit } from '@angular/core' ;
import { Attendee } from '../../../models' ;
import { EventService } from '../../services/event.service' ;
import { Observable } from 'rxjs' ;
@ Component ({
selector : 'app-event' ,
templateUrl : './event.component.html' ,
styleUrls : [ './event.component.scss' ]
})
export class EventComponent implements OnInit {
attendees$ : Observable < Attendee []>;
constructor ( private eventService : EventService ) {}
ngOnInit () {
this .getAttendees ();
}
getAttendees () {
this .attendees$ = this . eventService .getAttendees ();
}
addAttendee (attendee : Attendee ) {
this .eventService
.addAttendee (attendee)
.subscribe (() => this .getAttendees ());
}
}
StackBlitz Link