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 16, 2017
1 parent 35bbeb2 commit 17618d1
Show file tree
Hide file tree
Showing 24 changed files with 788 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('');
});
});
15 changes: 15 additions & 0 deletions public/docs/_examples/rxjs/ts/app/add-hero.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<h2>ADD HERO</h2>
<form [formGroup]="form" (ngSubmit)="save(form.value)">
<p>
*Name: <input type="text" formControlName="name"><br>
<span *ngIf="showErrors && form.get('name').errors" class="error">Name is required</span>
</p>
<p>
Description: <input type="text" formControlName="description">
</p>
<p>
<button type="submit" [disabled]="showErrors">Save</button>
</p>
</form>

<span *ngIf="success">The hero has been added</span>
74 changes: 74 additions & 0 deletions public/docs/_examples/rxjs/ts/app/add-hero.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// #docplaster
// #docregion
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/do';
import { Component, OnInit, OnDestroy, AfterViewInit, ViewChildren, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup, FormControlName, Validators } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

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

@Component({
moduleId: module.id,
templateUrl: 'add-hero.component.html',
styles: [ '.error { color: red }' ]
})
export class AddHeroComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChildren(FormControlName, { read: ElementRef }) formControls: ElementRef[];

form: FormGroup;
sub: Subscription;
showErrors: boolean = false;
submitted: boolean = false;
success: boolean;

constructor(
private formBuilder: FormBuilder,
private heroService: HeroService
) {}

ngOnInit() {
this.form = this.formBuilder.group({
name: ['', [Validators.required]],
description: ['']
});
}

ngAfterViewInit() {
const controlBlurs: Observable<Event>[] = this.formControls.map(field => Observable.fromEvent(field.nativeElement, 'blur'));

this.sub = Observable.merge(
this.form.valueChanges,
...controlBlurs
)
.debounceTime(300)
.subscribe(() => this.checkErrors());
}

checkErrors() {
if (!this.form.valid) {
this.showErrors = true;
}
}

save(model: any) {
this.success = false;
this.submitted = true;

this.heroService.addHero(model)
.do(() => {
this.success = true;
this.submitted = false;
})
.subscribe();
}

ngOnDestroy() {
this.sub.unsubscribe();
}
}
34 changes: 34 additions & 0 deletions public/docs/_examples/rxjs/ts/app/api-error-handler.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

export interface ApiError {
message: string;
}

@Injectable()
export class ApiErrorHandlerService {
handle(resp: Response): Observable<Error> {
return Observable.of(resp)
.switchMap(response => {

let error: ApiError;

try {
error = response.json().error;
} catch (e) {
if (response.status === 404) {
error = {
message: 'The requested resource was not found'
};
} else {
error = {
message: 'An unknown error has occurred'
};
}
}

return Observable.throw(error);
});
}
}
22 changes: 22 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,22 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AddHeroComponent } from './add-hero.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroSearchComponent } from './hero-search.component';
import { HeroListComponent } from './hero-list.component';

const appRoutes: Routes = [
{ path: 'heroes/add', component: AddHeroComponent },
{ path: 'heroes/search', component: HeroSearchComponent },
{ path: 'heroes', component: HeroListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{ path: '', redirectTo: '/heroes', pathMatch: 'full' },
];

@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
20 changes: 20 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,20 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';

@Component({
selector: 'my-app',
template: `
<h1 class="title">RxJS in Angular</h1>
<a routerLink="/heroes">Heroes</a><br>
<a routerLink="/heroes/add">Add Hero</a><br>
<a routerLink="/heroes/search">Hero Search</a>
<router-outlet></router-outlet>
<loading-component></loading-component>
`
})
export class AppComponent {
}
48 changes: 48 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,48 @@
// #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 { AddHeroComponent } from './add-hero.component';
import { LoadingComponent } from './loading.component';
import { HeroSearchComponent } from './hero-search.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.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';
import { ApiErrorHandlerService } from './api-error-handler.service';

@NgModule({
imports: [
BrowserModule,
HttpModule,
AppRoutingModule,
ReactiveFormsModule,
InMemoryWebApiModule.forRoot(InMemoryDataService)
],
declarations: [
AppComponent,
AddHeroComponent,
LoadingComponent,
HeroSearchComponent,
HeroDetailComponent,
HeroListComponent
],
providers: [
HeroService,
LoadingService,
ApiErrorHandlerService
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
// #enddocregion
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 AppEvent {
type: string;
message: string;
}

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

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

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

next() {
this.events$.next(this._events);
}
}
57 changes: 57 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,57 @@
// #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="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="error">
No hero found
</div>
`
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
loading: boolean = true;
error: boolean;

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

ngOnInit() {
this.route.params
.do(() => this.loading = true)
.switchMap((params: Params) =>
this.heroService.getHero(params['id'])
.catch(error => {
this.error = true;

return Observable.of(null);
})
)
.do(() => this.loading = false)
.subscribe((hero: Hero) => this.hero = hero);
}
}
31 changes: 31 additions & 0 deletions public/docs/_examples/rxjs/ts/app/hero-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// #docplaster
// #docregion
import 'rxjs/add/operator/toPromise';
import { Component, OnInit } from '@angular/core';

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

@Component({
template: `
<h2>HEROES</h2>
<ul class="items">
<li *ngFor="let hero of heroes">
<span class="badge">{{ hero.id }}</span> {{ hero.name }}
</li>
</ul>
`
})
export class HeroListComponent implements OnInit {
heroes: Hero[];

constructor(
private service: HeroService
) {}

ngOnInit() {
this.service.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
}
// #enddocregion
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">
<h2>HERO SEARCH</h2>
<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>
Loading

0 comments on commit 17618d1

Please sign in to comment.