fix(docs-infra): align `universal` example with `toh-pt6` (#36483)
As mentioned in the `universal` guide, the `toh-pt6` examples is the starting poitn for the `universal` example. However, the two examples had become out-of-sync, because some fixes/changes were made to the Tour-of-Heroes examples. This commit ports these changes to the `universal` example. PR Close #36483
This commit is contained in:
parent
3ce7c87cbd
commit
4d9da9b0a1
|
@ -1,7 +1,6 @@
|
||||||
/* AppComponent's private CSS styles */
|
/* AppComponent's private CSS styles */
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
color: #999;
|
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -18,7 +17,7 @@ nav a {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
nav a:visited, a:link {
|
nav a:visited, a:link {
|
||||||
color: #607D8B;
|
color: #334953;
|
||||||
}
|
}
|
||||||
nav a:hover {
|
nav a:hover {
|
||||||
color: #039be5;
|
color: #039be5;
|
||||||
|
|
|
@ -14,8 +14,6 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||||
import { HeroesComponent } from './heroes/heroes.component';
|
import { HeroesComponent } from './heroes/heroes.component';
|
||||||
import { HeroSearchComponent } from './hero-search/hero-search.component';
|
import { HeroSearchComponent } from './hero-search/hero-search.component';
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
import { MessageService } from './message.service';
|
|
||||||
import { MessagesComponent } from './messages/messages.component';
|
import { MessagesComponent } from './messages/messages.component';
|
||||||
|
|
||||||
// #docregion platform-detection
|
// #docregion platform-detection
|
||||||
|
@ -32,6 +30,10 @@ import { isPlatformBrowser } from '@angular/common';
|
||||||
FormsModule,
|
FormsModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|
||||||
|
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
|
||||||
|
// and returns simulated server responses.
|
||||||
|
// Remove it when a real server is ready to receive requests.
|
||||||
HttpClientInMemoryWebApiModule.forRoot(
|
HttpClientInMemoryWebApiModule.forRoot(
|
||||||
InMemoryDataService, { dataEncapsulation: false }
|
InMemoryDataService, { dataEncapsulation: false }
|
||||||
)
|
)
|
||||||
|
@ -44,7 +46,6 @@ import { isPlatformBrowser } from '@angular/common';
|
||||||
MessagesComponent,
|
MessagesComponent,
|
||||||
HeroSearchComponent
|
HeroSearchComponent
|
||||||
],
|
],
|
||||||
providers: [ HeroService, MessageService ],
|
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|
|
@ -34,7 +34,7 @@ h4 {
|
||||||
color: #eee;
|
color: #eee;
|
||||||
max-height: 120px;
|
max-height: 120px;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
background-color: #607D8B;
|
background-color: #3f525c;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.module:hover {
|
.module:hover {
|
||||||
|
|
|
@ -8,4 +8,4 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hero-search></hero-search>
|
<app-hero-search></app-hero-search>
|
||||||
|
|
|
@ -18,6 +18,6 @@ export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
getHeroes(): void {
|
getHeroes(): void {
|
||||||
this.heroService.getHeroes()
|
this.heroService.getHeroes()
|
||||||
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
|
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ button {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
cursor: hand;
|
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div *ngIf="hero">
|
<div *ngIf="hero">
|
||||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
<h2>{{hero.name | uppercase}} Details</h2>
|
||||||
<div><span>id: </span>{{hero.id}}</div>
|
<div><span>id: </span>{{hero.id}}</div>
|
||||||
<div>
|
<div>
|
||||||
<label>name:
|
<label>name:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
|
||||||
styleUrls: [ './hero-detail.component.css' ]
|
styleUrls: [ './hero-detail.component.css' ]
|
||||||
})
|
})
|
||||||
export class HeroDetailComponent implements OnInit {
|
export class HeroDetailComponent implements OnInit {
|
||||||
hero: Hero;
|
@Input() hero: Hero;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -33,7 +33,7 @@ export class HeroDetailComponent implements OnInit {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
this.heroService.updateHero(this.hero)
|
this.heroService.updateHero(this.hero)
|
||||||
.subscribe(() => this.goBack());
|
.subscribe(() => this.goBack());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<div id="search-component">
|
<div id="search-component">
|
||||||
<h4>Hero Search</h4>
|
<h4><label for="search-box">Hero Search</label></h4>
|
||||||
|
|
||||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||||
|
|
||||||
<ul class="search-result">
|
<ul class="search-result">
|
||||||
<li *ngFor="let hero of heroes | async" >
|
<li *ngFor="let hero of heroes$ | async" >
|
||||||
<a routerLink="/detail/{{hero.id}}">
|
<a routerLink="/detail/{{hero.id}}">
|
||||||
{{hero.name}}
|
{{hero.name}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -10,12 +10,12 @@ import { Hero } from '../hero';
|
||||||
import { HeroService } from '../hero.service';
|
import { HeroService } from '../hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hero-search',
|
selector: 'app-hero-search',
|
||||||
templateUrl: './hero-search.component.html',
|
templateUrl: './hero-search.component.html',
|
||||||
styleUrls: [ './hero-search.component.css' ]
|
styleUrls: [ './hero-search.component.css' ]
|
||||||
})
|
})
|
||||||
export class HeroSearchComponent implements OnInit {
|
export class HeroSearchComponent implements OnInit {
|
||||||
heroes: Observable<Hero[]>;
|
heroes$: Observable<Hero[]>;
|
||||||
private searchTerms = new Subject<string>();
|
private searchTerms = new Subject<string>();
|
||||||
|
|
||||||
constructor(private heroService: HeroService) {}
|
constructor(private heroService: HeroService) {}
|
||||||
|
@ -26,7 +26,7 @@ export class HeroSearchComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.heroes = this.searchTerms.pipe(
|
this.heroes$ = this.searchTerms.pipe(
|
||||||
// wait 300ms after each keystroke before considering the term
|
// wait 300ms after each keystroke before considering the term
|
||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Injectable, Inject, Optional } from '@angular/core';
|
import { Injectable, Inject, Optional } from '@angular/core';
|
||||||
import { APP_BASE_HREF } from '@angular/common';
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
import { HttpClient, HttpHeaders }from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { catchError, map, tap } from 'rxjs/operators';
|
import { catchError, map, tap } from 'rxjs/operators';
|
||||||
|
@ -8,30 +8,31 @@ import { catchError, map, tap } from 'rxjs/operators';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { MessageService } from './message.service';
|
import { MessageService } from './message.service';
|
||||||
|
|
||||||
const httpOptions = {
|
|
||||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
|
||||||
};
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class HeroService {
|
export class HeroService {
|
||||||
|
|
||||||
private heroesUrl = 'api/heroes'; // URL to web api
|
private heroesUrl = 'api/heroes'; // URL to web api
|
||||||
|
|
||||||
|
httpOptions = {
|
||||||
|
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||||
|
};
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
@Optional() @Inject(APP_BASE_HREF) origin?: string) {
|
@Optional() @Inject(APP_BASE_HREF) origin?: string) {
|
||||||
this.heroesUrl = `${origin}${this.heroesUrl}`;
|
this.heroesUrl = `${origin || ''}${this.heroesUrl}`;
|
||||||
}
|
}
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
/** GET heroes from the server */
|
/** GET heroes from the server */
|
||||||
getHeroes (): Observable<Hero[]> {
|
getHeroes(): Observable<Hero[]> {
|
||||||
return this.http.get<Hero[]>(this.heroesUrl)
|
return this.http.get<Hero[]>(this.heroesUrl)
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(heroes => this.log('fetched heroes')),
|
tap(_ => this.log('fetched heroes')),
|
||||||
catchError(this.handleError('getHeroes', []))
|
catchError(this.handleError<Hero[]>('getHeroes', []))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,9 @@ export class HeroService {
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
|
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
|
||||||
tap(_ => this.log(`found heroes matching "${term}"`)),
|
tap(x => x.length ?
|
||||||
|
this.log(`found heroes matching "${term}"`) :
|
||||||
|
this.log(`no heroes matching "${term}"`)),
|
||||||
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -73,29 +76,27 @@ export class HeroService {
|
||||||
//////// Save methods //////////
|
//////// Save methods //////////
|
||||||
|
|
||||||
/** POST: add a new hero to the server */
|
/** POST: add a new hero to the server */
|
||||||
addHero (name: string): Observable<Hero> {
|
addHero(hero: Hero): Observable<Hero> {
|
||||||
const hero = { name };
|
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||||
|
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
|
||||||
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
|
|
||||||
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
|
|
||||||
catchError(this.handleError<Hero>('addHero'))
|
catchError(this.handleError<Hero>('addHero'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** DELETE: delete the hero from the server */
|
/** DELETE: delete the hero from the server */
|
||||||
deleteHero (hero: Hero | number): Observable<Hero> {
|
deleteHero(hero: Hero | number): Observable<Hero> {
|
||||||
const id = typeof hero === 'number' ? hero : hero.id;
|
const id = typeof hero === 'number' ? hero : hero.id;
|
||||||
const url = `${this.heroesUrl}/${id}`;
|
const url = `${this.heroesUrl}/${id}`;
|
||||||
|
|
||||||
return this.http.delete<Hero>(url, httpOptions).pipe(
|
return this.http.delete<Hero>(url, this.httpOptions).pipe(
|
||||||
tap(_ => this.log(`deleted hero id=${id}`)),
|
tap(_ => this.log(`deleted hero id=${id}`)),
|
||||||
catchError(this.handleError<Hero>('deleteHero'))
|
catchError(this.handleError<Hero>('deleteHero'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PUT: update the hero on the server */
|
/** PUT: update the hero on the server */
|
||||||
updateHero (hero: Hero): Observable<any> {
|
updateHero(hero: Hero): Observable<any> {
|
||||||
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
|
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||||
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
||||||
catchError(this.handleError<any>('updateHero'))
|
catchError(this.handleError<any>('updateHero'))
|
||||||
);
|
);
|
||||||
|
@ -107,7 +108,7 @@ export class HeroService {
|
||||||
* @param operation - name of the operation that failed
|
* @param operation - name of the operation that failed
|
||||||
* @param result - optional value to return as the observable result
|
* @param result - optional value to return as the observable result
|
||||||
*/
|
*/
|
||||||
private handleError<T> (operation = 'operation', result?: T) {
|
private handleError<T>(operation = 'operation', result?: T) {
|
||||||
return (error: any): Observable<T> => {
|
return (error: any): Observable<T> => {
|
||||||
|
|
||||||
// TODO: send the error to remote logging infrastructure
|
// TODO: send the error to remote logging infrastructure
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.heroes a {
|
.heroes a {
|
||||||
color: #888;
|
color: #333;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.heroes a:hover {
|
.heroes a:hover {
|
||||||
color:#607D8B;
|
color: #607D8B;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heroes .badge {
|
.heroes .badge {
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
font-size: small;
|
font-size: small;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.8em 0.7em 0 0.7em;
|
padding: 0.8em 0.7em 0 0.7em;
|
||||||
background-color: #607D8B;
|
background-color: #405061;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: -1px;
|
left: -1px;
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 4px 0 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
button {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||||
</a>
|
</a>
|
||||||
<button class="delete" title="delete hero"
|
<button class="delete" title="delete hero"
|
||||||
(click)="delete(hero);$event.stopPropagation()">x</button>
|
(click)="delete(hero)">x</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -25,17 +25,15 @@ export class HeroesComponent implements OnInit {
|
||||||
add(name: string): void {
|
add(name: string): void {
|
||||||
name = name.trim();
|
name = name.trim();
|
||||||
if (!name) { return; }
|
if (!name) { return; }
|
||||||
this.heroService.addHero(name)
|
this.heroService.addHero({ name } as Hero)
|
||||||
.subscribe(hero => {
|
.subscribe(hero => {
|
||||||
this.heroes.push(hero);
|
this.heroes.push(hero);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(hero: Hero): void {
|
delete(hero: Hero): void {
|
||||||
this.heroService.deleteHero(hero)
|
this.heroes = this.heroes.filter(h => h !== hero);
|
||||||
.subscribe(() => {
|
this.heroService.deleteHero(hero).subscribe();
|
||||||
this.heroes = this.heroes.filter(h => h !== hero);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||||
|
import { Hero } from './hero';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
export class InMemoryDataService implements InMemoryDbService {
|
export class InMemoryDataService implements InMemoryDbService {
|
||||||
createDb() {
|
createDb() {
|
||||||
const heroes = [
|
const heroes = [
|
||||||
|
@ -16,4 +21,13 @@ export class InMemoryDataService implements InMemoryDbService {
|
||||||
];
|
];
|
||||||
return {heroes};
|
return {heroes};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overrides the genId method to ensure that a hero always has an id.
|
||||||
|
// If the heroes array is empty,
|
||||||
|
// the method below returns the initial number (11).
|
||||||
|
// if the heroes array is not empty, the method below returns the highest
|
||||||
|
// hero id + 1.
|
||||||
|
genId(heroes: Hero[]): number {
|
||||||
|
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class MessageService {
|
export class MessageService {
|
||||||
messages: string[] = [];
|
messages: string[] = [];
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,6 @@ button:disabled {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
button.clear {
|
button.clear {
|
||||||
color: #888;
|
color: #333;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,5 @@ if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
|
Loading…
Reference in New Issue