Skip to content

Commit 661f175

Browse files
committed
Tutorial 8/g - Search by name
1 parent 737aa3b commit 661f175

7 files changed

+134
-1
lines changed

src/app/app.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ import { MessageService } from './message.service';
1313
import { AppRoutingModule } from './/app-routing.module';
1414
import { DashboardComponent } from './dashboard/dashboard.component';
1515
import { InMemoryDataService } from './in-memory-data.service';
16+
import { HeroSearchComponent } from './hero-search/hero-search.component';
1617

1718
@NgModule({
1819
declarations: [
1920
AppComponent,
2021
HeroesComponent,
2122
HeroDetailComponent,
2223
MessagesComponent,
23-
DashboardComponent
24+
DashboardComponent,
25+
HeroSearchComponent
2426
],
2527
imports: [
2628
BrowserModule,

src/app/dashboard/dashboard.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ <h4>{{hero.name}}</h4>
66
</div>
77
</a>
88
</div>
9+
10+
<app-hero-search></app-hero-search>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* HeroSearch private styles */
2+
.search-result li {
3+
border-bottom: 1px solid gray;
4+
border-left: 1px solid gray;
5+
border-right: 1px solid gray;
6+
width:195px;
7+
height: 16px;
8+
padding: 5px;
9+
background-color: white;
10+
cursor: pointer;
11+
list-style-type: none;
12+
}
13+
14+
.search-result li:hover {
15+
background-color: #607D8B;
16+
}
17+
18+
.search-result li a {
19+
color: #888;
20+
display: block;
21+
text-decoration: none;
22+
}
23+
24+
.search-result li a:hover {
25+
color: white;
26+
}
27+
.search-result li a:active {
28+
color: white;
29+
}
30+
#search-box {
31+
width: 200px;
32+
height: 20px;
33+
}
34+
35+
36+
ul.search-result {
37+
margin-top: 0;
38+
padding-left: 0;
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<div id="search-component">
2+
<h4>Hero Search</h4>
3+
4+
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
5+
6+
<ul class="search-result">
7+
<li *ngFor="let hero of heroes$ | async" >
8+
<a routerLink="/detail/{{hero.id}}">
9+
{{hero.name}}
10+
</a>
11+
</li>
12+
</ul>
13+
</div>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { HeroSearchComponent } from './hero-search.component';
4+
5+
describe('HeroSearchComponent', () => {
6+
let component: HeroSearchComponent;
7+
let fixture: ComponentFixture<HeroSearchComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ HeroSearchComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(HeroSearchComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
import { Observable, Subject } from 'rxjs';
4+
5+
import {
6+
debounceTime, distinctUntilChanged, switchMap
7+
} from 'rxjs/operators';
8+
9+
import { Hero } from '../hero';
10+
import { HeroService } from '../hero.service';
11+
12+
@Component({
13+
selector: 'app-hero-search',
14+
templateUrl: './hero-search.component.html',
15+
styleUrls: [ './hero-search.component.css' ]
16+
})
17+
export class HeroSearchComponent implements OnInit {
18+
heroes$: Observable<Hero[]>;
19+
private searchTerms = new Subject<string>();
20+
21+
constructor(private heroService: HeroService) {}
22+
23+
// Push a search term into the observable stream.
24+
search(term: string): void {
25+
this.searchTerms.next(term);
26+
}
27+
28+
ngOnInit(): void {
29+
this.heroes$ = this.searchTerms.pipe(
30+
// wait 300ms after each keystroke before considering the term
31+
debounceTime(300),
32+
33+
// ignore new term if same as previous term
34+
distinctUntilChanged(),
35+
36+
// switch to new search observable each time the term changes
37+
switchMap((term: string) => this.heroService.searchHeroes(term)),
38+
);
39+
}
40+
}

src/app/hero.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ export class HeroService {
7070
);
7171
}
7272

73+
/* GET heroes whose name contains search term */
74+
searchHeroes(term: string): Observable<Hero[]> {
75+
if (!term.trim()) {
76+
// if not search term, return empty hero array.
77+
return of([]);
78+
}
79+
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
80+
tap(_ => this.log(`found heroes matching "${term}"`)),
81+
catchError(this.handleError<Hero[]>('searchHeroes', []))
82+
);
83+
}
84+
7385
/**
7486
* Handle Http operation that failed.
7587
* Let the app continue.

0 commit comments

Comments
 (0)