1

In a parent component I have a stream of Tour[] tours_filtered: Observable<Tour[]> which I assign in the subscribe function of an http request

this.api.getTours().subscribe(
  result => {
    this.tours_filtered = of(result.tours);
  }
)

in the view I display the stream using the async pipe

<app-tour-box [tour]="tour" *ngFor="let tour of tours_filtered | async"></app-tour-box>

Up to here all works as expected. In a child component I have an input text which emits the value inserted by the user to filtering the array of Tour by title.

In the parent component I listen for the emitted values in a function, I switch to new stream of Tour[] filtered by that value using switchMap

onSearchTitle(term: string) {
  this.tours_filtered.pipe(
    switchMap( 
      (tours) => of( tours.filter((tour) => tour.name.toLowerCase().includes(term)) )
    )
  )
}

I thought that the async pipe was constantly listening to reflect the changes to the array to which it was applied and so I thought I didn't have to subscribe in the function above, but nothing change in the view when I type in the input to filtering the results.

The results are updating correctly if I assign the new stream to the original array in the subscribe function

    onSearchTitle(term: string) {
        this.tours_filtered.pipe(
          switchMap((tours) => of(tours.filter((tour) => tour.name.toLowerCase().includes(term))))
        ).subscribe( val => { this.tours_filtered = of(val); })
      }

Is this procedure correct? Could I avoid to subscribe because I already use the async pipe? There is a better way to reach my goal?

EDITED:

Maybe I found a solution, I have to reassing a new stream to the variable just like this

onSearchTitle(term: string) {
    this.tours_filtered = of(this.city.tours).pipe(
      switchMap((tours) => of(tours.filter((tour) => tour.name.toLowerCase().includes(term))))
    );
  }

and I don't need to subscribe again, the results in the view change according to the search term typed by the user. Is this the correct way?

1 Answer 1

1

I think in your situation the solution should work as follows:

onSearchTitle(term: string) {
  this._searchTerm = term;
  this.tours_filtered = of( 
    this.city.tours.filter((tour) => tour.name.toLowerCase().includes(term))
  )
}

Because in your example you don't change the observable which is used in ngFor. Thus it's not working.

However, I don't see the reason of using observables here unless this is the first step and you're going to fetch this data from server in future


UPDATE

The best solution for you would be to consider your input as an observable and watch for the changes:

// your.component.ts
export class AppComponent  {

  searchTerm$ = new BehaviorSubject<string>('');
  results = this.search(this.searchTerm$);

  search(terms: Observable<string>) {
    return terms
      .pipe(
        debounceTime(400),
        distinctUntilChanged(),
        switchMap(term => {
          return of(this.city.tours.filter((tour) => tour.name.toLowerCase().includes(term)))
        }
        )
      )
  }

}
// your.template.html
...
<input type="" (input)="searchTerm$.next($event.target.value)">
...

Additionally it would be great to add debounceTime and distinctUntilChanged for better user experience and less search requests.

See full example for the details. Also please, refer to this article for more detailed explanations

Sign up to request clarification or add additional context in comments.

4 Comments

sorry my mistake, in the original version the swithMap operator was switchMap((tours) => of(tours.filter((tour) => tour.name.toLowerCase().includes(term)))) but didn't work, I use observable to take advantage of the nature of switchMap which cancel the previous requests while user is typing the text to search
your solution is very nice if the input was in the same component with the other logic but it is in a child component in which I have collected all the possible filters to apply. I should create an @Input() property to pass in the tours array and the relative @Output() with the Observable<Tour[]> filtered or share a service between.
Please, provide a StackBlitz example with smth close to your example so I could fork it
Thanks, I solved sharing a service and now the source of observables are the controls in the child component , the parent only deals with displaying the results

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.