Skip to content

Commit 3388295

Browse files
committed
added reactive services patterns for data layer
1 parent 7313c8c commit 3388295

2 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Injectable } from '@angular/core';
2+
import { Http, Response } from '@angular/http';
3+
import { Observable } from 'rxjs/Observable';
4+
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
5+
import 'rxjs/add/operator/map';
6+
import 'rxjs/add/operator/filter';
7+
8+
// Example of a custom Data Type for our Notes model
9+
export interface User {
10+
firstName: string;
11+
lastName: string;
12+
email: string;
13+
gender: string;
14+
}
15+
16+
/**
17+
* Statefull service which provides an Observable-based API with Observable Data
18+
*
19+
* Main Features:
20+
* 1. Statefull:
21+
* - This service is statefull and exposes a state variable which is the Observable called user$
22+
*
23+
* 2. Observable-based API:
24+
* - For any component which is using this service, the service ensures isolation
25+
* from how the data is retrieved - in this case, Angular HTTP service.
26+
*
27+
* When to use:
28+
* - when we want service act as a permanent storage for our fetched data
29+
*
30+
*
31+
* Example usage:
32+
*
33+
* @Component({
34+
* selector: 'home',
35+
* template: `
36+
* <div class='main'>
37+
* <user-detail [user]="user$ | async"></user-detail>
38+
* </div>`
39+
+ })
40+
* public class HomeComponent {
41+
* user$: Observable<User>
42+
* constructor(private userService: StatefullObservableDataService) {
43+
* this.user$ = userService.user$;
44+
* }
45+
* }
46+
*
47+
*/
48+
@Injectable()
49+
export class StatefullObservableDataService {
50+
static API_URL = `/api/v1/user/`;
51+
52+
// Subject implements Observer and the Observable interfaces.
53+
// This means we can emit values and also use it as an Observable.
54+
// In the following case our subject is a Behavior Subject, meaning it stores the
55+
// last value emitted and sends it immediately upon subscription.
56+
private subject: BehaviorSubject<User> = new BehaviorSubject<User>(null);
57+
58+
// Our Observable data which is exposed as a state variable.
59+
// This Observable is derived from a Subject, and a filter is
60+
// applied to prevent the null value from being propagated.
61+
public user$: Observable<User> = this.subject.asObservable().filter(user => !!user);
62+
63+
constructor(private http: Http) {
64+
// The user data is retrieved from the backend using the HTTP service.
65+
// Once the data is available, we emit a new value of the subject.
66+
this.getUserInfo()
67+
.subscribe((user: User) => this.subject.next(user));
68+
}
69+
70+
/**
71+
* Get user information from backend API service
72+
*
73+
* @return {Observable<User>}
74+
*/
75+
private getUserInfo(): Observable<User> {
76+
return this.http
77+
.get(StatefullObservableDataService.API_URL)
78+
.map((res: Response) => res.json());
79+
}
80+
81+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Injectable } from '@angular/core';
2+
import { Http, Response } from '@angular/http';
3+
import { Observable } from 'rxjs/Observable';
4+
import 'rxjs/add/operator/map';
5+
6+
// Example of a custom Data Type for our Notes model
7+
export interface Note {
8+
title: string;
9+
body: string;
10+
author: string;
11+
}
12+
13+
/**
14+
* Stateless service which provides an Observable-based API
15+
*
16+
* Main Features:
17+
* 1. Statelessnes:
18+
* - It has no member variables for holding data.
19+
*
20+
* 2. Observable-based API:
21+
* - All methods return Observables of a custom data type (e.g. `Note` or `Note[]`).
22+
* - For any component which is using this service, the service ensures isolation
23+
* from how the data is retrieved - in this case, Angular HTTP service.
24+
*
25+
* When to use:
26+
* - for example: CRUD interaction with a RESTful API backend.
27+
* - very simple usage (e.g. get some data from the backend API and pass it to the View Layer)
28+
* - no advanced features (e.g. caching responses from APIs inside service)
29+
*
30+
*
31+
* Example usage:
32+
*
33+
* @Component({
34+
* selector: 'home',
35+
* template: `
36+
* <div class='main'>
37+
* <note-detail *ngIf="isLoaded" [note]="note"></note-detail>
38+
* </div>`
39+
* })
40+
* public class HomeComponent {
41+
* public note: Note;
42+
* public isLoaded: boolean = false;
43+
*
44+
* constructor(private noteService: StatelessObservableService) {
45+
* this.is
46+
* this.noteService.findOne(1)
47+
* .subscribe((note: Note) => {
48+
* this.isLoaded = true;
49+
* this.note = note;
50+
* }, (error) => {
51+
* this.isLoaded = false;
52+
* console.log(`Could not load note ${error}`);
53+
* });
54+
* }
55+
* }
56+
*
57+
*/
58+
@Injectable()
59+
export class StatelessObservableService {
60+
static API_URL: string = '/api/v1/note/';
61+
62+
constructor(private http: Http) { }
63+
64+
/**
65+
* Get a listing of the resource.
66+
*
67+
* @returns {Observable<Note[]>}
68+
*/
69+
public findAll(): Observable<Note[]> {
70+
return this.http
71+
.get(StatelessObservableService.API_URL)
72+
.map((res: Response) => res.json());
73+
}
74+
75+
/**
76+
* Find one resource by `id`.
77+
*
78+
* @param {string} id
79+
* @return {Observable<Note>}
80+
*/
81+
public findOne(id: string): Observable<Note> {
82+
return this.http
83+
.get(`{StatelessObservableService.API_URL}/id`)
84+
.map((res: Response) => res.json());
85+
}
86+
87+
}

0 commit comments

Comments
 (0)