简体   繁体   中英

Angular async pipe with input

i'm getting a list from json which i want to display and being able to filter using an input box. so far it's working but the list only displays once i input something into the searchbox not before that. i would like the list being displayed at the start.I tried to set the search box value at start with empty string but it's not working. looked up the web didn't find the way to achieve. Any help would be appreciated.

my component html:


<h4><label for="search-box">Hero Search</label></h4>

<input #searchBox id="search-box" (input)=search(searchBox.value)  />
<ul class="heroes">
  <li *ngFor="let hero of heroes$ | async">
    <a routerLink="/detail/{{hero.id}}">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </a>
    <button class="delete" title="delete hero"
  (click)="delete(hero)">Delete</button>
  </li>
</ul>
<div>
  <label>Hero name:
    <input #heroName />
  </label>
  <!-- (click) passes input value to add() and then clears the input -->
  <button (click)="add(heroName.value); heroName.value=''">
    add
  </button>
</div>

component ts:

  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes$: Observable<Hero[]>=null;
  heroes: Hero[];
  selectedHero: Hero;
  private searchTerms = new Subject<string>();
  constructor(private heroService: HeroService) {
  }

  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 term changes
        switchMap((term: string) => this.heroService.searchHeroes(term)),
      );


    }

  // Push a search term into the observable stream.
  search(term: string): void {
    this.searchTerms.next(term);

  }

  getHeroes(): void {
    this.heroService.getHeroes()
      .subscribe(heroes => this.heroes = heroes);

  }

service ts:

/* GET heroes whose name contains search term */
  searchHeroes(term: string): Observable<Hero[]> {
    if (!term.trim()) {
      // if not search term, return full array.
      return this.getHeroes();
    }
    return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
      tap(x => x.length ?
        this.log(`found heroes matching "${term}"`) :
        this.log(`no heroes matching "${term}"`)),
      catchError(this.handleError<Hero[]>('searchHeroes', []))
    );
  }

You can declare your searchTerms as an BehaviorSubject instead of an simple Subject. The main difference is that as an subcriber of that BehaviorSubject, you will get the last value emitted from that observable no matter when your subscription will happen. Be aware that BehaviorSubject need an initial value that will be emitted when you initialized it.

private searchTerms: BehaviorSubject<string> = new BehaviorSubject<string>('');

Because in your case it's connected to the input event, you need to automatically trigger a first value to start your pipeline. You can do this by using the startWith operator

  this.heroes$ = this.searchTerms.pipe(
    // wait 300ms after each keystroke before considering the term
    debounceTime(300),

    startWith(''), // <-- start your pipe with an empty string

    // ignore new term if same as previous term
    distinctUntilChanged(),

    // switch to new search observable each time the term changes
    switchMap((term: string) => this.heroService.searchHeroes(term)),
  );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM