Skip to content

Instantly share code, notes, and snippets.

@jrgcubano
Last active March 24, 2017 19:08
Show Gist options
  • Save jrgcubano/8e557590844a73a853668c17ddebdf44 to your computer and use it in GitHub Desktop.
Save jrgcubano/8e557590844a73a853668c17ddebdf44 to your computer and use it in GitHub Desktop.
ReactiveExtensions Angular 2 examples for http calls (cached, delay, recursive)
<h1>{{title}}</h1>
<div *ngFor="let user of usersA">
{{user.name.first}}
</div>
<button (click)="loadSubscriptionA()">Load A</button>
<button (click)="unloadSubscriptionA()">Unload A</button>
<div *ngFor="let user of usersB">
{{user.name.first}}
</div>
<button (click)="loadSubscriptionB()">Load B</button>
<button (click)="unloadSubscriptionB()">Unload B</button>
<div *ngFor="let user of usersC">
{{user.name.first}}
</div>
<button (click)="loadSubscriptionC()">Load C</button>
<button (click)="unloadSubscriptionC()">Unload C</button>
import { Component } from '@angular/core';
import { UsersService } from '../shared/services/index';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
usersA = [];
usersB = [];
usersC = [];
subscriptionA;
subscriptionB;
subscriptionC;
title;
constructor(private _usersService:UsersService) {}
loadSubscriptionA() {
this.subscriptionA =
this._usersService.getUsers()
.subscribe(
res => {
if(!res.isError) {
console.log("Data A: " + res.data)
this.usersA = res.data;
} else {
console.log("Error by result A: " + res.data)
}
},
err => console.log("Error By Throw A: " + err));
}
unloadSubscriptionA() {
this.subscriptionA.unsubscribe();
this.usersA = null;
}
loadSubscriptionB() {
this.subscriptionB =
this._usersService.getUsers()
.subscribe(
res => {
if(!res.isError) {
console.log("Data B: " + res.data)
this.usersB = res.data;
} else {
console.log("Error by result B: " + res.data)
}
},
err => console.log("Error By Throw B: " + err));
}
unloadSubscriptionB() {
this.subscriptionB.unsubscribe();
this.usersB = null;
}
loadSubscriptionC() {
this.subscriptionC =
this._usersService.getUsers()
.subscribe(
res => {
if(!res.isError) {
console.log("Data C: " + res.data)
this.usersC = res.data;
} else {
console.log("Error by result C: " + res.data)
}
},
err => console.log("Error By Throw C: " + err));
}
unloadSubscriptionC() {
this.subscriptionC.unsubscribe();
this.usersC = null;
}
ngOnInit(){
this.title = 'app works!!!';
console.log("Hola!!");
// this.loadSubscriptionA();
// this.loadSubscriptionB();
// this.loadSubscriptionC();
}
ngOnDestroy(){
this.unloadSubscriptionA();
this.unloadSubscriptionB();
this.unloadSubscriptionC();
console.log('Destroyed');
}
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { UsersService } from '../shared/services/index';
@NgModule({
imports: [ BrowserModule, FormsModule, HttpModule ],
declarations: [ AppComponent ],
providers: [ UsersService ],
bootstrap: [AppComponent]
})
export class AppModule { }
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>RxServiceCache</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
import { AppModule } from './app/app.module';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/expand';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/observable/of';
// import 'rxjs/add/operator/debounceTime';
// import 'rxjs/add/operator/distinctUntilChanged';
// import 'rxjs/add/operator/publishReplay';
import 'rxjs/Rx';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
import { Injectable } from '@angular/core';
import { Http, Response} from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/expand';
@Injectable()
export class UsersService {
_users:Observable<any> = null;
constructor(private _http: Http) {
}
clearCache(){
this._users = null;
}
// Recursive iteration to get data with delay (not cached)
// getApiData() {
// return this._http.get('https://randomuser.me/api/?page=1&results=10')
// .map((res:Response) => res.json().results)
// .timestamp()
// .do(i => console.log(`[debug] Data retreived from server @${i.timestamp}`));
// }
// getUsers() {
// if(!this._users){
// this._users =
// this.getApiData()
// .expand(_ => Observable.of([])
// .delay(5000)
// .timestamp()
// .do(i => console.log(`[debug] Waiting 5sec for next getData ${i.timestamp}`))
// .concatMap(() => this.getApiData())
// );
// }
// return this._users;
// }
// Cached only with retention delay
// getApiData() {
// return this._http.get('https://randomuser.me/api/?page=1&results=10')
// .map((res:Response) => res.json().results)
// // .timestamp()
// // .do(i => console.log(`[debug] Data retreived from server @${i.timestamp}`))
// .catch(err => Observable.of(err));
// }
// getUsers() {
// if(!this._users){
// this._users =
// Observable.defer(() => this.getApiData())
// // .timestamp()
// // .do(i => console.log(`[debug] Waiting 5sec for next getData ${i.timestamp}`))
// .publishReplay(1, 3000)
// .refCount()
// .take(1);
// }
// return this._users;
// }
// Recursive iteration with delay and cached retention delay
// getApiData() {
// return this._http.get('https://randomuser.me/api/?page=1&results=10')
// .map((res:Response) => res.json().results)
// .timestamp()
// .do(i => console.log(`[debug] Data retreived from server @${i.timestamp}`));
// }
// getUsers() {
// if(!this._users){
// this._users =
// Observable.of([])
// .mergeMap(i =>
// this.getApiData()
// .expand(_ => Observable.of([])
// .delay(5000)
// .timestamp()
// .do(i => console.log(`[debug] Waiting 5sec for next getData ${i.timestamp}`))
// .concatMap(() => this.getApiData())
// )
// )
// .publishReplay(1, 5000)
// .refCount();
// //.take(1);
// }
// return this._users;
// Recursive iteration with delay and cached retention delay (clean version without "do" and "timestamp" logs!!!)
getApiData() {
return this._http.get('https://randomuser.me/api/?page=1&results=10')
.map((res:Response) => new UsersResult(res.json().results, false))
// (3) Error strategy: Pass errors as result on the same observable
// and retain subscriptions with the same delay until connection back
.catch((error:any) =>
Observable.of(new UsersResult((error.json().error || 'Server error'), true))
);;
}
getUsers() {
if(!this._users){
this._users =
Observable.of([])
.mergeMap(i => this.getApiData()
.expand(_ =>
Observable.of([])
.delay(5000)
.concatMap(() => this.getApiData())
)
)
.publishReplay(1, 5000)
.refCount()
// (1) Error strategy: Just throw and break subscriptions for all consumers
// .catch((error:any) => {
// this._users = null;
// return Observable.throw(error.json().error || 'Server error')
// });
// (2) Error strategy: Pass errors as result on the same observable and stop subscriptions
//.catch((error:any) =>
// Observable.of(new UsersResult((error.json().error || 'Server error'), true))
//);
}
return this._users;
}
}
@jrgcubano
Copy link
Author

TODO: Testing with https://blog.hyphe.me/rxjs-testing-in-real-world-applications/ (using spies or net approach injecting scheduler on all services to control time!).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment