Search code examples
angulartypescriptrxjs

How to compose observables using a ViewChildren event and void methods in angular 12?


I'm trying to create a typeahead that will call a service on keyup as the user types into an input box. My input is inside an *ngIf, so I'm using ViewChildren and subscribing to it's changes to get the input. The method I am calling does not return a value, so I'm unsure how to properly compose rxjs operators to do this.

This is my current solution:

private searchBoxPlaceholder: ElementRef;
@ViewChildren("searchbox", { read: ElementRef }) elementRefs: QueryList<ElementRef>;

public ngAfterViewInit(): any {
  this.elementRefs.changes.subscribe(() => {
      if (this.elementRefs.toArray().length) {
        fromEvent(this.elementRefs.first.nativeElement, "keyup")
          .pipe(
            filter(Boolean),
            debounceTime(2000),
            distinctUntilChanged(),
            tap(() => {
              this.filterSearch(this.elementRefs.first.nativeElement.value);
            })
          )
          .subscribe();
      }
    });
  }

It works, but I feel like there is a better way to structure this without nesting subscribes. I'm also unsure if tap() is the best rxjs operator to use in this case. I've tried various combinations of switchMap, mergMap, concatMap, but since filterSearch does not return an Observable it's not working for me.


Solution

  • Something like this... untested :) FromEvent returns an Observable so you can use this within a switchMap and avoid the multiple subscribes. Also added takeUntil and a filter for the array length

     public ngAfterViewInit(): any {
        this.elementRefs.changes.pipe(
          filter(() => this.elementRefs.toArray().length > 0),
          switchMap(() => {
            return fromEvent(this.elementRefs.first.nativeElement, 'keyup')
              .pipe(
                filter(Boolean),
                debounceTime(2000),
                distinctUntilChanged(),
                tap(() => {
                  this.filterSearch(this.elementRefs.first.nativeElement.value);
                })
              );
          }),
          takeUntil(this.onDestroy$)
        ).subscribe();
    
      }