docs: add universal guide (#18707)
- based on original effort in PR 17573 PR Close #18707
This commit is contained in:

committed by
Chuck Jazdzewski

parent
963a4d0dc8
commit
0b0d25fa33
@ -9,7 +9,8 @@ const routes: Routes = [
|
||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'detail/:id', component: HeroDetailComponent },
|
||||
{ path: 'heroes', component: HeroesComponent }
|
||||
{ path: 'heroes', component: HeroesComponent },
|
||||
{ path: '**', redirectTo: '/dashboard' }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* #docregion */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
color: #999;
|
||||
|
@ -1,5 +1,3 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
|
@ -1,54 +1,62 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion v1, v2
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
// #docregion simple
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
// #enddocregion v1
|
||||
// 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 { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { InMemoryDataService } from './in-memory-data.service';
|
||||
|
||||
// #docregion v1
|
||||
import { AppComponent } from './app.component';
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { HeroService } from './hero.service';
|
||||
// #enddocregion v1, v2
|
||||
import { HeroSearchComponent } from './hero-search.component';
|
||||
// #docregion v1, v2
|
||||
|
||||
// #enddocregion simple
|
||||
// #docregion platform-detection
|
||||
import { PLATFORM_ID, APP_ID, Inject } from '@angular/core';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
|
||||
// #enddocregion platform-detection
|
||||
// #docregion simple
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({
|
||||
appId: 'toh-universal'
|
||||
}),
|
||||
// #docregion browsermodule
|
||||
BrowserModule.withServerTransition({ appId: 'uni' }),
|
||||
// #enddocregion browsermodule
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
// #enddocregion v1
|
||||
// #docregion in-mem-web-api
|
||||
InMemoryWebApiModule.forRoot(InMemoryDataService),
|
||||
// #enddocregion in-mem-web-api
|
||||
// #docregion v1
|
||||
HttpClientModule,
|
||||
HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService),
|
||||
AppRoutingModule
|
||||
],
|
||||
// #docregion search
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DashboardComponent,
|
||||
HeroDetailComponent,
|
||||
HeroesComponent,
|
||||
// #enddocregion v1, v2
|
||||
HeroSearchComponent
|
||||
// #docregion v1, v2
|
||||
],
|
||||
// #enddocregion search
|
||||
providers: [ HeroService ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {
|
||||
|
||||
// #enddocregion simple
|
||||
// #docregion platform-detection
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: Object,
|
||||
@Inject(APP_ID) private appId: string) {
|
||||
const platform = isPlatformBrowser(platformId) ?
|
||||
'on the server' : 'in the browser';
|
||||
console.log(`Running ${platform} with appId=${appId}`);
|
||||
}
|
||||
// #enddocregion platform-detection
|
||||
// #docregion simple
|
||||
}
|
||||
// #enddocregion simple
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* #docregion */
|
||||
[class*='col-'] {
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
|
@ -1,7 +1,6 @@
|
||||
<!-- #docregion -->
|
||||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
|
||||
<a *ngFor="let hero of heroes | async" [routerLink]="['/detail', hero.id]" class="col-1-4">
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
|
@ -1,22 +1,23 @@
|
||||
// #docregion , search
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
import 'rxjs/add/operator/map';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: [ './dashboard.component.css' ]
|
||||
})
|
||||
// #enddocregion search
|
||||
export class DashboardComponent implements OnInit {
|
||||
heroes: Hero[] = [];
|
||||
heroes: Observable<Hero[]>;
|
||||
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.heroService.getHeroes()
|
||||
.then(heroes => this.heroes = heroes.slice(1, 5));
|
||||
this.heroes = this.heroService.getHeroes()
|
||||
.map(heroes => heroes.slice(1, 5));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* #docregion */
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
@ -25,6 +24,6 @@ button:hover {
|
||||
}
|
||||
button:disabled {
|
||||
background-color: #eee;
|
||||
color: #ccc;
|
||||
color: #ccc;
|
||||
cursor: auto;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
<!-- #docregion -->
|
||||
<div *ngIf="hero">
|
||||
<h2>{{hero.name}} details!</h2>
|
||||
<div>
|
||||
@ -8,7 +7,5 @@
|
||||
<input [(ngModel)]="hero.name" placeholder="name" />
|
||||
</div>
|
||||
<button (click)="goBack()">Back</button>
|
||||
<!-- #docregion save -->
|
||||
<button (click)="save()">Save</button>
|
||||
<!-- #enddocregion save -->
|
||||
</div>
|
||||
|
@ -1,4 +1,3 @@
|
||||
// #docregion
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
@ -27,12 +26,11 @@ export class HeroDetailComponent implements OnInit {
|
||||
.subscribe(hero => this.hero = hero);
|
||||
}
|
||||
|
||||
// #docregion save
|
||||
save(): void {
|
||||
this.heroService.update(this.hero)
|
||||
.then(() => this.goBack());
|
||||
this.heroService
|
||||
.update(this.hero)
|
||||
.subscribe(() => this.goBack());
|
||||
}
|
||||
// #enddocregion save
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* #docregion */
|
||||
.search-result{
|
||||
border-bottom: 1px solid gray;
|
||||
border-left: 1px solid gray;
|
||||
|
@ -1,4 +1,3 @@
|
||||
<!-- #docregion -->
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
|
@ -1,20 +1,14 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
// #docregion rxjs-imports
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
// Observable class extensions
|
||||
import 'rxjs/add/observable/of';
|
||||
|
||||
// Observable operators
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
// #enddocregion rxjs-imports
|
||||
|
||||
import { HeroSearchService } from './hero-search.service';
|
||||
import { Hero } from './hero';
|
||||
@ -26,27 +20,20 @@ import { Hero } from './hero';
|
||||
providers: [HeroSearchService]
|
||||
})
|
||||
export class HeroSearchComponent implements OnInit {
|
||||
// #docregion search
|
||||
heroes: Observable<Hero[]>;
|
||||
// #enddocregion search
|
||||
// #docregion searchTerms
|
||||
private searchTerms = new Subject<string>();
|
||||
// #enddocregion searchTerms
|
||||
|
||||
constructor(
|
||||
private heroSearchService: HeroSearchService,
|
||||
private router: Router) {}
|
||||
// #docregion searchTerms
|
||||
|
||||
// Push a search term into the observable stream.
|
||||
search(term: string): void {
|
||||
this.searchTerms.next(term);
|
||||
}
|
||||
// #enddocregion searchTerms
|
||||
// #docregion search
|
||||
|
||||
ngOnInit(): void {
|
||||
this.heroes = this.searchTerms
|
||||
this.heroes = this.searchTerms.asObservable()
|
||||
.debounceTime(300) // wait 300ms after each keystroke before considering the term
|
||||
.distinctUntilChanged() // ignore if next search term is same as previous
|
||||
.switchMap(term => term // switch to new observable each time the term changes
|
||||
@ -60,7 +47,6 @@ export class HeroSearchComponent implements OnInit {
|
||||
return Observable.of<Hero[]>([]);
|
||||
});
|
||||
}
|
||||
// #enddocregion search
|
||||
|
||||
gotoDetail(hero: Hero): void {
|
||||
let link = ['/detail', hero.id];
|
||||
|
@ -1,20 +1,28 @@
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { Hero } from './hero';
|
||||
|
||||
// #docregion class
|
||||
@Injectable()
|
||||
export class HeroSearchService {
|
||||
|
||||
constructor(private http: Http) {}
|
||||
private searchUrl = 'api/heroes/?name='; // URL to web api
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Optional() @Inject(APP_BASE_HREF) origin: string) {
|
||||
this.searchUrl = (origin || '') + this.searchUrl;
|
||||
}
|
||||
|
||||
search(term: string): Observable<Hero[]> {
|
||||
return this.http
|
||||
.get(`api/heroes/?name=${term}`)
|
||||
.map(response => response.json().data as Hero[]);
|
||||
.get(this.searchUrl + term)
|
||||
.map((data: any) => data.data as Hero[]);
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
|
@ -1,87 +1,64 @@
|
||||
// #docplaster
|
||||
// #docregion , imports
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers, Http } from '@angular/http';
|
||||
import { Injectable, Inject, Optional } from '@angular/core';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpHeaders, HttpClient } from '@angular/common/http';
|
||||
|
||||
// #docregion rxjs
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
// #enddocregion rxjs
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Hero } from './hero';
|
||||
// #enddocregion imports
|
||||
|
||||
const headers = new HttpHeaders({'Content-Type': 'application/json'});
|
||||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
|
||||
// #docregion update
|
||||
private headers = new Headers({'Content-Type': 'application/json'});
|
||||
// #enddocregion update
|
||||
// #docregion getHeroes
|
||||
private heroesUrl = 'api/heroes'; // URL to web api
|
||||
|
||||
constructor(private http: Http) { }
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Optional() @Inject(APP_BASE_HREF) origin: string) {
|
||||
this.heroesUrl = (origin || '') + this.heroesUrl;
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
getHeroes(): Promise<Hero[]> {
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
return this.http.get(this.heroesUrl)
|
||||
// #docregion to-promise
|
||||
.toPromise()
|
||||
// #enddocregion to-promise
|
||||
// #docregion to-data
|
||||
.then(response => response.json().data as Hero[])
|
||||
// #enddocregion to-data
|
||||
// #docregion catch
|
||||
.map((data: any) => data.data as Hero[])
|
||||
.catch(this.handleError);
|
||||
// #enddocregion catch
|
||||
}
|
||||
|
||||
// #enddocregion getHeroes
|
||||
|
||||
// #docregion getHero
|
||||
getHero(id: number): Promise<Hero> {
|
||||
getHero(id: number): Observable<Hero> {
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
return this.http.get(url)
|
||||
.toPromise()
|
||||
.then(response => response.json().data as Hero)
|
||||
.map((data: any) => data.data as Hero)
|
||||
.catch(this.handleError);
|
||||
}
|
||||
// #enddocregion getHero
|
||||
|
||||
// #docregion delete
|
||||
delete(id: number): Promise<void> {
|
||||
delete(id: number): Observable<void> {
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
return this.http.delete(url, {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(() => null)
|
||||
return this.http.delete(url, { headers })
|
||||
.catch(this.handleError);
|
||||
}
|
||||
// #enddocregion delete
|
||||
|
||||
// #docregion create
|
||||
create(name: string): Promise<Hero> {
|
||||
create(name: string): Observable<Hero> {
|
||||
return this.http
|
||||
.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(res => res.json().data)
|
||||
.post(this.heroesUrl, { name: name }, { headers })
|
||||
.map((data: any) => data.data)
|
||||
.catch(this.handleError);
|
||||
}
|
||||
// #enddocregion create
|
||||
// #docregion update
|
||||
|
||||
update(hero: Hero): Promise<Hero> {
|
||||
update(hero: Hero): Observable<Hero> {
|
||||
const url = `${this.heroesUrl}/${hero.id}`;
|
||||
return this.http
|
||||
.put(url, JSON.stringify(hero), {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(() => hero)
|
||||
.put(url, hero, { headers })
|
||||
.catch(this.handleError);
|
||||
}
|
||||
// #enddocregion update
|
||||
|
||||
// #docregion getHeroes, handleError
|
||||
private handleError(error: any): Promise<any> {
|
||||
private handleError(error: any): Observable<any> {
|
||||
console.error('An error occurred', error); // for demo purposes only
|
||||
return Promise.reject(error.message || error);
|
||||
throw error;
|
||||
}
|
||||
// #enddocregion getHeroes, handleError
|
||||
}
|
||||
|
||||
|
@ -21,31 +21,27 @@ export class HeroesComponent implements OnInit {
|
||||
getHeroes(): void {
|
||||
this.heroService
|
||||
.getHeroes()
|
||||
.then(heroes => this.heroes = heroes);
|
||||
.subscribe(heroes => this.heroes = heroes);
|
||||
}
|
||||
|
||||
// #docregion add
|
||||
add(name: string): void {
|
||||
name = name.trim();
|
||||
if (!name) { return; }
|
||||
this.heroService.create(name)
|
||||
.then(hero => {
|
||||
.subscribe(hero => {
|
||||
this.heroes.push(hero);
|
||||
this.selectedHero = null;
|
||||
});
|
||||
}
|
||||
// #enddocregion add
|
||||
|
||||
// #docregion delete
|
||||
delete(hero: Hero): void {
|
||||
this.heroService
|
||||
.delete(hero.id)
|
||||
.then(() => {
|
||||
.subscribe(() => {
|
||||
this.heroes = this.heroes.filter(h => h !== hero);
|
||||
if (this.selectedHero === hero) { this.selectedHero = null; }
|
||||
});
|
||||
}
|
||||
// #enddocregion delete
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getHeroes();
|
||||
|
Reference in New Issue
Block a user