Skip to content
This repository has been archived by the owner on Dec 4, 2017. It is now read-only.

Commit

Permalink
docs(rxjs): Added developer guide on Observables
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonroberts committed Jan 8, 2017
1 parent aff39d2 commit aa3e1e6
Show file tree
Hide file tree
Showing 27 changed files with 1,023 additions and 0 deletions.
12 changes: 12 additions & 0 deletions public/docs/_examples/rxjs/e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict'; // necessary for es6 output in node

import { browser
/*, element, by, ElementFinder*/
} from 'protractor';

describe('RxJS', function () {

beforeAll(function () {
browser.get('');
});
});
19 changes: 19 additions & 0 deletions public/docs/_examples/rxjs/ts/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroSearchComponent } from './hero-search.component';

const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'hero/search', component: HeroSearchComponent },
{ path: 'hero/:id', component: HeroDetailComponent }
];

@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
15 changes: 15 additions & 0 deletions public/docs/_examples/rxjs/ts/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';

@Component({
selector: 'my-app',
template: `
<h1 class="title">RxJS in Angular</h1>
<router-outlet></router-outlet>
<loading-component></loading-component>
`
})
export class AppComponent {
}
50 changes: 50 additions & 0 deletions public/docs/_examples/rxjs/ts/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HomeComponent } from './home.component';
import { HeroesReadyComponent } from './heroes-ready.component';
import { CounterComponent } from './counter.component';
import { FormFieldComponent } from './form-field.component';
import { LoadingComponent } from './loading.component';
import { HeroSearchComponent } from './hero-search.component';
import { HeroDetailComponent } from './hero-detail.component';

import { LoadingService } from './loading.service';
import { HeroService } from './hero.service';

// Imports for loading & configuring the in-memory web api
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';

@NgModule({
imports: [
BrowserModule,
HttpModule,
AppRoutingModule,
ReactiveFormsModule,
InMemoryWebApiModule.forRoot(InMemoryDataService)
],
declarations: [
AppComponent,
HomeComponent,
HeroesReadyComponent,
CounterComponent,
FormFieldComponent,
LoadingComponent,
HeroSearchComponent,
HeroDetailComponent
],
providers: [
HeroService,
LoadingService
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
// #enddocregion
33 changes: 33 additions & 0 deletions public/docs/_examples/rxjs/ts/app/counter.component.1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// #docplaster
// #docregion
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';

@Component({
selector: 'counter-component',
template: `
<p>
Hero Counter: {{ count }}
<button (click)="increment()">Increment</button>
</p>
`
})
export class CounterComponent implements OnInit, OnDestroy {
count: number = 0;
counter$ = new Subject();
sub: Subscription;

ngOnInit() {
this.sub = this.counter$.subscribe();
}

increment() {
this.counter$.next(this.count++);
}

ngOnDestroy() {
this.sub.unsubscribe();
}
}
33 changes: 33 additions & 0 deletions public/docs/_examples/rxjs/ts/app/counter.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// #docplaster
// #docregion
import 'rxjs/add/operator/takeUntil';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Component({
selector: 'counter-component',
template: `
<p>
Counter: {{ count }}
<button (click)="increment()">Increment</button>
</p>
`
})
export class CounterComponent implements OnInit, OnDestroy {
count: number = 0;
counter$ = new Subject();
destroy$: Subject<any> = new Subject();

ngOnInit() {
this.counter$.takeUntil(this.destroy$).subscribe();
}

increment() {
this.counter$.next(this.count++);
}

ngOnDestroy() {
this.destroy$.next();
}
}
27 changes: 27 additions & 0 deletions public/docs/_examples/rxjs/ts/app/event-aggregator.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

export interface Event {
type: string;
message: string;
}

@Injectable()
export class EventAggregatorService {
_events: Event[];
events$: BehaviorSubject<Event[]> = new BehaviorSubject<any>([]);

add(event: Event) {
this._events.push(event);
this.next();
}

clear() {
this._events = [];
this.next();
}

next() {
this.events$.next(this._events);
}
}
25 changes: 25 additions & 0 deletions public/docs/_examples/rxjs/ts/app/form-field.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// #docplaster
// #docregion
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/fromEvent';
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Component({
selector: 'form-field-component',
template: `
<p>
<input type="text" #name>
<span *ngIf="blurred$ | async">Blurred</span>
</p>
`
})
export class FormFieldComponent implements OnInit {
@ViewChild('name', { read: ElementRef }) name: ElementRef;

blurred$: Observable<boolean>;

ngOnInit() {
this.blurred$ = Observable.fromEvent(this.name.nativeElement, 'blur');
}
}
59 changes: 59 additions & 0 deletions public/docs/_examples/rxjs/ts/app/hero-detail.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// #docplaster
// #docregion
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { HeroService } from './hero.service';
import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';

@Component({
template: `
<div *ngIf="loading">
Loading Hero...
</div>
<div *ngIf="loaded && hero">
<h3>HEROES</h3>
<div>
<label>Id: </label>{{ hero.id }}
</div>
<div>
<label>Name: </label>
<input placeholder="name" [value]="hero.name"/>
</div>
</div>
<div *ngIf="loaded && !hero">
No hero found
</div>
`
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
loading: boolean = true;
loaded: boolean;

constructor(
private heroService: HeroService,
private route: ActivatedRoute
) {}

ngOnInit() {
this.route.params
.do(() => {
this.loading = true;
this.loaded = false;
})
.switchMap((params: Params) =>
this.heroService.getHero(params['id'])
.catch(() => Observable.of(null))
)
.do(() => {
this.loading = false;
this.loaded = true;
})
.subscribe(hero => this.hero = hero);
}
}
16 changes: 16 additions & 0 deletions public/docs/_examples/rxjs/ts/app/hero-search.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* #docregion */
.search-result{
border-bottom: 1px solid gray;
border-left: 1px solid gray;
border-right: 1px solid gray;
width:195px;
height: 20px;
padding: 5px;
background-color: white;
cursor: pointer;
}

.search-box{
width: 200px;
height: 20px;
}
12 changes: 12 additions & 0 deletions public/docs/_examples/rxjs/ts/app/hero-search.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!-- #docregion -->
<div id="search-component">
<h4>Hero Search</h4>
<form [formGroup]="form">
<input formControlName="searchTerms" class="search-box" />
</form>
<div>
<div *ngFor="let hero of heroes | async" class="search-result">
<a [routerLink]="['/hero', hero.id]">{{ hero.name }}</a>
</div>
</div>
</div>
67 changes: 67 additions & 0 deletions public/docs/_examples/rxjs/ts/app/hero-search.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// #docplaster
// #docregion
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/observable/merge';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

import { HeroService } from './hero.service';
import { Hero } from './hero';

@Component({
moduleId: module.id,
selector: 'hero-search',
templateUrl: 'hero-search.component.html',
styleUrls: [ 'hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit, OnDestroy {
heroes$: Observable<Hero[]>;
destroy$: Subject<any> = new Subject();
form: FormGroup;

constructor(
private heroService: HeroService,
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router
) {}

ngOnInit(): void {
this.form = this.formBuilder.group({
searchTerms: ['']
});

const searchTerms$: Observable<string> = this.form.valueChanges
.debounceTime(300)
.map(model => model.searchTerms);

const querySearch$: Observable<string> = this.route.queryParams
.map((params: Params) => params['q'])
.do(searchTerms => this.form.patchValue({
searchTerms
}));

this.heroes$ = Observable.merge(searchTerms$, querySearch$)
.distinctUntilChanged()
.takeUntil(this.destroy$)
.do(q => this.router.navigate(['./'], { queryParams: { q }, relativeTo: this.route }))
.switchMap(term => term
? this.heroService.search(term)
: Observable.of<Hero[]>([])
)
.catch(error => {
return Observable.of<Hero[]>([]);
});
}

ngOnDestroy() {
this.destroy$.next();
}
}
Loading

0 comments on commit aa3e1e6

Please sign in to comment.