0
votes

I am very new in Angualar and I am following this official Angular tutorial: https://angular.io/tutorial/toh-pt6

and I have some doubts about how it works, this is my commented code of the HeroSearchComponent class:

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 {

  // It is an Observable emitting an array of Hero objects:
  heroes$: Observable<Hero[]>;

  /**
   * The searchTerms property is declared as an RxJS Subject.
   * A Subject is both a source of observable values and an Observable itself. 
   * You can subscribe to a Subject as you would any Observable.
   * You can also push values into that Observable by calling its next(value) method as the search() method does.
   */
  private searchTerms = new Subject<string>();

  constructor(private heroService: HeroService) {}

  /**
   * Push a search term into the observable stream.
   * Every time the user types in the textbox, the binding calls search() with the textbox value, a "search term". 
   * The searchTerms becomes an Observable emitting a steady stream of search terms. 
   */ 
  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 term changes
      switchMap((term: string) => this.heroService.searchHeroes(term)),
    );
  }
}

This class is used to search the heros from the hero-search.component.html containing:

<div id="search-component">
  <h4>Hero Search</h4>

  <!--
    As the user types in the search box, a keyup event binding calls 
    the component's search() method with the new search box value
  -->
  <input #searchBox id="search-box" (keyup)="search(searchBox.value)" />

  <ul class="search-result">
    <!--
        The *ngFor iterates over a list called heroes$, not heroes.
        The $ is a convention that indicates heroes$ is an Observable, not an array.
        The *ngFor can't do anything with an Observable. 
        But there's also a pipe character (|) followed by async, which identifies Angular's AsyncPipe.
        The AsyncPipe subscribes to an Observable automatically so you 
        won't have to do so in the component class.
    -->
    <li *ngFor="let hero of heroes$ | async" >
      <a routerLink="/detail/{{hero.id}}">
        {{hero.name}}
      </a>
    </li>
  </ul>
</div>

Here my interpretation, I am absolutly not sure if it is correct or not.

The heroes$ field of the HeroSearchComponent is not an array but it is an Observable emitting an array. This because we live in an asyncronous world so we have to wait that the server provide the response.

In the view I can't directly iterate on an array but I have to wait that I receive the response (when the previous Observable emit the array of Hero), this is done using the asyncornous pipe to register on this event:

<li *ngFor="let hero of heroes$ | async" >

This should be correct...

Then into the view I have:

<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />

When the user insert a new character into the input tag it is called the component search() method passing the value inserted into the input tag ("a", "ab", "abc", "ab", etcetc).

This is the search() method:

search(term: string): void {
  this.searchTerms.next(term);
}

Looking at it is not directly performing a search calling a backend service but it is pushing the received new term into the searchTerms.

This searchTerms object is a Subject. From what I have understood a Subject is an object that can emit event (as an Observable**) and at the same time can receive a stream of evens (in this case a stream of terms eacht time that the user insert a new char into the view search input).

But...how and where it is performed the call to obtain the heroes list matching with the inserted term?

My idea is the following one: it is not directly do in this method but there is something like a pre-setted behavior. This is done when this class is instanced by the ngOnInit() method:

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)),
  );
}

That basically says something like: call the heroService.searchHeroes(term) method (the method that perform the search on my backend) passing the term each time that the searchTerms receive\emit an event (that is the last term to search). In theory this pipe is setted to do it not for every char inserted but every 300ms to avoid to perform much call).

I am not sure about this mechanism and how exactly this pipe works.

What is the correct and complete interpretation?

1
So far all your interpretations are correct. The mentioned pipe (| async) makes an subscription to the given observable and returns the given value from the observable. Another possibility (or better what the async pipe does under the hood) is calling the subscribe method of the observable. The differences between both approaches can be read here. If you need more informations about the subscription itself take a look hereOliver

1 Answers

0
votes

What is pipe() function in Angular 2?

We have pipes concept in Angular and pipes() function in RxJS

1) Pipes in Angular: A pipe takes in data as input and transforms it to the desired output https://angular.io/guide/pipes

2) pipe() function in RxJS: You can use pipes to link operators together. Pipes let you combine multiple functions into a single function.

The pipe() function takes as its arguments the functions you want to combine, and returns a new function that, when executed, runs the composed functions in sequence. https://angular.io/guide/rx-library (search for pipes in this URL, you can find the same)