7 changed files with 134 additions and 2 deletions
@ -1,9 +1,11 @@ |
|||
<h3>Top Heroes</h3> |
|||
<div class="grid grid-pad"> |
|||
<a *ngFor="let hero of heroes" class="col-1-4" |
|||
routerLink="/detail/{{hero.id}}"> |
|||
routerLink="/detail/{{hero.id}}"> |
|||
<div class="module hero"> |
|||
<h4>{{hero.name}}</h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
|
|||
<app-hero-search></app-hero-search> |
|||
|
@ -0,0 +1,39 @@ |
|||
/* HeroSearch private styles */ |
|||
.search-result li { |
|||
border-bottom: 1px solid gray; |
|||
border-left: 1px solid gray; |
|||
border-right: 1px solid gray; |
|||
width: 195px; |
|||
height: 16px; |
|||
padding: 5px; |
|||
background-color: white; |
|||
cursor: pointer; |
|||
list-style-type: none; |
|||
} |
|||
|
|||
.search-result li:hover { |
|||
background-color: #607D8B; |
|||
} |
|||
|
|||
.search-result li a { |
|||
color: #888; |
|||
display: block; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
.search-result li a:hover { |
|||
color: white; |
|||
} |
|||
.search-result li a:active { |
|||
color: white; |
|||
} |
|||
#search-box { |
|||
width: 200px; |
|||
height: 20px; |
|||
} |
|||
|
|||
|
|||
ul.search-result { |
|||
margin-top: 0; |
|||
padding-left: 0; |
|||
} |
@ -0,0 +1,13 @@ |
|||
<div id="search-component"> |
|||
<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" > |
|||
<a routerLink="/detail/{{hero.id}}"> |
|||
{{hero.name}} |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
@ -0,0 +1,25 @@ |
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
|||
|
|||
import { HeroSearchComponent } from './hero-search.component'; |
|||
|
|||
describe('HeroSearchComponent', () => { |
|||
let component: HeroSearchComponent; |
|||
let fixture: ComponentFixture<HeroSearchComponent>; |
|||
|
|||
beforeEach(async(() => { |
|||
TestBed.configureTestingModule({ |
|||
declarations: [ HeroSearchComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
})); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(HeroSearchComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
@ -0,0 +1,40 @@ |
|||
import { Component, OnInit } from '@angular/core'; |
|||
|
|||
import { Observable, Subject } from 'rxjs'; |
|||
|
|||
import { |
|||
debounceTime, distinctUntilChanged, switchMap |
|||
} from 'rxjs/operators'; |
|||
|
|||
import { Hero } from '../hero'; |
|||
import { HeroService } from '../hero.service'; |
|||
|
|||
@Component({ |
|||
selector: 'app-hero-search', |
|||
templateUrl: './hero-search.component.html', |
|||
styleUrls: ['./hero-search.component.css'] |
|||
}) |
|||
export class HeroSearchComponent implements OnInit { |
|||
heroes$: Observable<Hero[]>; |
|||
private searchTerms = new Subject<string>(); |
|||
|
|||
constructor(private heroService: HeroService) { } |
|||
|
|||
// Push a search term into the observable stream.
|
|||
search(term: string): void { |
|||
this.searchTerms.next(term); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.heroes$ = this.searchTerms.pipe( |
|||
// wait 300ms after each keystroke before considering the term
|
|||
debounceTime(300), |
|||
|
|||
// ignore new term if same as previous term
|
|||
distinctUntilChanged(), |
|||
|
|||
// switch to new search observable each time the terme changes
|
|||
switchMap((term: string) => this.heroService.searchHeroes(term)), |
|||
); |
|||
} |
|||
} |
Loading…
Reference in new issue