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 */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h2 {
|
||||
|
@ -18,7 +17,7 @@ nav a {
|
|||
border-radius: 4px;
|
||||
}
|
||||
nav a:visited, a:link {
|
||||
color: #607D8B;
|
||||
color: #334953;
|
||||
}
|
||||
nav a:hover {
|
||||
color: #039be5;
|
||||
|
|
|
@ -14,8 +14,6 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
|||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
import { HeroesComponent } from './heroes/heroes.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';
|
||||
|
||||
// #docregion platform-detection
|
||||
|
@ -32,6 +30,10 @@ import { isPlatformBrowser } from '@angular/common';
|
|||
FormsModule,
|
||||
AppRoutingModule,
|
||||
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(
|
||||
InMemoryDataService, { dataEncapsulation: false }
|
||||
)
|
||||
|
@ -44,7 +46,6 @@ import { isPlatformBrowser } from '@angular/common';
|
|||
MessagesComponent,
|
||||
HeroSearchComponent
|
||||
],
|
||||
providers: [ HeroService, MessageService ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule {
|
||||
|
|
|
@ -34,7 +34,7 @@ h4 {
|
|||
color: #eee;
|
||||
max-height: 120px;
|
||||
min-width: 120px;
|
||||
background-color: #607D8B;
|
||||
background-color: #3f525c;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.module:hover {
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<hero-search></hero-search>
|
||||
<app-hero-search></app-hero-search>
|
||||
|
|
|
@ -18,6 +18,6 @@ export class DashboardComponent implements OnInit {
|
|||
|
||||
getHeroes(): void {
|
||||
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;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<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 { Location } from '@angular/common';
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
|
|||
styleUrls: [ './hero-detail.component.css' ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
hero: Hero;
|
||||
@Input() hero: Hero;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -33,7 +33,7 @@ export class HeroDetailComponent implements OnInit {
|
|||
this.location.back();
|
||||
}
|
||||
|
||||
save(): void {
|
||||
save(): void {
|
||||
this.heroService.updateHero(this.hero)
|
||||
.subscribe(() => this.goBack());
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<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)" />
|
||||
|
||||
<ul class="search-result">
|
||||
<li *ngFor="let hero of heroes | async" >
|
||||
<li *ngFor="let hero of heroes$ | async" >
|
||||
<a routerLink="/detail/{{hero.id}}">
|
||||
{{hero.name}}
|
||||
</a>
|
||||
|
|
|
@ -10,12 +10,12 @@ import { Hero } from '../hero';
|
|||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-search',
|
||||
selector: 'app-hero-search',
|
||||
templateUrl: './hero-search.component.html',
|
||||
styleUrls: [ './hero-search.component.css' ]
|
||||
})
|
||||
export class HeroSearchComponent implements OnInit {
|
||||
heroes: Observable<Hero[]>;
|
||||
heroes$: Observable<Hero[]>;
|
||||
private searchTerms = new Subject<string>();
|
||||
|
||||
constructor(private heroService: HeroService) {}
|
||||
|
@ -26,7 +26,7 @@ export class HeroSearchComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.heroes = this.searchTerms.pipe(
|
||||
this.heroes$ = this.searchTerms.pipe(
|
||||
// wait 300ms after each keystroke before considering the term
|
||||
debounceTime(300),
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Injectable, Inject, Optional } from '@angular/core';
|
||||
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 { catchError, map, tap } from 'rxjs/operators';
|
||||
|
@ -8,30 +8,31 @@ import { catchError, map, tap } from 'rxjs/operators';
|
|||
import { Hero } from './hero';
|
||||
import { MessageService } from './message.service';
|
||||
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class HeroService {
|
||||
|
||||
private heroesUrl = 'api/heroes'; // URL to web api
|
||||
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private messageService: MessageService,
|
||||
@Optional() @Inject(APP_BASE_HREF) origin?: string) {
|
||||
this.heroesUrl = `${origin}${this.heroesUrl}`;
|
||||
this.heroesUrl = `${origin || ''}${this.heroesUrl}`;
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
/** GET heroes from the server */
|
||||
getHeroes (): Observable<Hero[]> {
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
return this.http.get<Hero[]>(this.heroesUrl)
|
||||
.pipe(
|
||||
tap(heroes => this.log('fetched heroes')),
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
tap(_ => this.log('fetched heroes')),
|
||||
catchError(this.handleError<Hero[]>('getHeroes', []))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,9 @@ export class HeroService {
|
|||
return of([]);
|
||||
}
|
||||
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', []))
|
||||
);
|
||||
}
|
||||
|
@ -73,29 +76,27 @@ export class HeroService {
|
|||
//////// Save methods //////////
|
||||
|
||||
/** POST: add a new hero to the server */
|
||||
addHero (name: string): Observable<Hero> {
|
||||
const hero = { name };
|
||||
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
|
||||
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
|
||||
addHero(hero: Hero): Observable<Hero> {
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
|
||||
catchError(this.handleError<Hero>('addHero'))
|
||||
);
|
||||
}
|
||||
|
||||
/** 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 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}`)),
|
||||
catchError(this.handleError<Hero>('deleteHero'))
|
||||
);
|
||||
}
|
||||
|
||||
/** PUT: update the hero on the server */
|
||||
updateHero (hero: Hero): Observable<any> {
|
||||
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
|
||||
updateHero(hero: Hero): Observable<any> {
|
||||
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
||||
catchError(this.handleError<any>('updateHero'))
|
||||
);
|
||||
|
@ -107,7 +108,7 @@ export class HeroService {
|
|||
* @param operation - name of the operation that failed
|
||||
* @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> => {
|
||||
|
||||
// TODO: send the error to remote logging infrastructure
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
|
||||
.heroes a {
|
||||
color: #888;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
display: block;
|
||||
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
|
||||
.heroes a:hover {
|
||||
color:#607D8B;
|
||||
color: #607D8B;
|
||||
}
|
||||
|
||||
.heroes .badge {
|
||||
|
@ -38,7 +38,7 @@
|
|||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color: #607D8B;
|
||||
background-color: #405061;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
|
@ -50,7 +50,7 @@
|
|||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.button {
|
||||
button {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||
</a>
|
||||
<button class="delete" title="delete hero"
|
||||
(click)="delete(hero);$event.stopPropagation()">x</button>
|
||||
(click)="delete(hero)">x</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -25,17 +25,15 @@ export class HeroesComponent implements OnInit {
|
|||
add(name: string): void {
|
||||
name = name.trim();
|
||||
if (!name) { return; }
|
||||
this.heroService.addHero(name)
|
||||
this.heroService.addHero({ name } as Hero)
|
||||
.subscribe(hero => {
|
||||
this.heroes.push(hero);
|
||||
});
|
||||
}
|
||||
|
||||
delete(hero: Hero): void {
|
||||
this.heroService.deleteHero(hero)
|
||||
.subscribe(() => {
|
||||
this.heroes = this.heroes.filter(h => h !== hero);
|
||||
});
|
||||
this.heroes = this.heroes.filter(h => h !== hero);
|
||||
this.heroService.deleteHero(hero).subscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InMemoryDataService implements InMemoryDbService {
|
||||
createDb() {
|
||||
const heroes = [
|
||||
|
@ -16,4 +21,13 @@ export class InMemoryDataService implements InMemoryDbService {
|
|||
];
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MessageService {
|
||||
messages: string[] = [];
|
||||
|
||||
|
|
|
@ -30,6 +30,6 @@ button:disabled {
|
|||
cursor: auto;
|
||||
}
|
||||
button.clear {
|
||||
color: #888;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ if (environment.production) {
|
|||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
|
|
Loading…
Reference in New Issue