简体   繁体   中英

pipe operator not behaving as expected RXJS

Please look at my component below the purpose to is to listen on changes to an input, which it does and then emit the value to the parent component. I created a pipe to only emit every so often and therby minimize the calls to the api, for some reason even though I can see through various console.log statements that it goes in the pipe, it emits the value on every change. What is it that I am missing:

import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output, KeyValueDiffers, DoCheck, KeyValueDiffer} from '@angular/core';
import {BehaviorSubject, Observable, of} from "rxjs";
import {debounceTime, distinctUntilChanged, map, skip, switchMap, takeUntil, tap} from "rxjs/operators";

@Component({
  selector: 'core-ui-typeahead-filter',
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './typeahead-filter.component.html',
})
export class TypeaheadFilterComponent implements DoCheck {
  @Input() id: string;
  @Input() name: string;
  @Input() caption: string;
  @Input() placeholder: string;
  @Input() cssClass: string;
  @Input() cssStyle: string;
  @Input() function: any;

  @Input() data: Observable<string[]>;
  differ: any;
  detectChange: string = '';
  // term$ = new BehaviorSubject<string>('');
  text$ = new Observable<string>();

  @Output() onTypeahead: EventEmitter<any> = new EventEmitter<any>();
  @Output() onSelect: EventEmitter<any> = new EventEmitter<any>();


  constructor(private differs: KeyValueDiffers) {
    this.differ = this.differs.find({}).create();
  }

  handleTypeahead = (text$: Observable<string>) =>
    text$.pipe(
      distinctUntilChanged(),
      debounceTime(500),
    ).subscribe((value) => {
      this.onTypeahead.emit(of(value))
    })


handleSelectItem(item) {
  this.onSelect.emit(item);
}

ngDoCheck() {
    const change = this.differ.diff(this);
    if (change) {
      change.forEachChangedItem(item => {
        if (item.key === 'detectChange'){
          console.log('item changed', item)
          this.text$ = of(item.currentValue);
          this.handleTypeahead(this.text$);
        }
      });
    }
  }

}

More background: There is an ngModel on the input linked to detectChange when it changes then the ngDoCheck is called and executes. Everything is done in observables so in the parent I can subscribe to the incoming events. EDIT ------------------------------------------------------------------- Tried the following solution based on my understanding of @ggradnig answer, sadly it skips over my pipe something seems wrong with it, really not sure what:

 handleTypeahead = (text$: Observable<string>) => {
    this.test.subscribe(this.text$);
    this.test.pipe(
      distinctUntilChanged(),
      debounceTime(500),
      // switchMap(value => text$)
    ).subscribe((value) => {
      tap(console.log('im inside the subscription',value))
      this.onTypeahead.emit(value)
    })
  }



handleSelectItem(item) {
  this.onSelect.emit(item);
}

ngDoCheck() {
    const change = this.differ.diff(this);
    if (change) {
      change.forEachChangedItem(item => {
        if (item.key === 'detectChange'){
          console.log('item changed', item)
          this.text$ = of(item.currentValue);
          this.handleTypeahead(this.test);
        }
      });
    }
  }

}

You can do the following -

export class TypeaheadFilterComponent implements DoCheck {
  @Input() id: string;
  @Input() name: string;
  @Input() caption: string;
  @Input() placeholder: string;
  @Input() cssClass: string;
  @Input() cssStyle: string;
  @Input() function: any;

  @Input() data: Observable<string[]>;
  differ: any;
  detectChange: string = '';
  // term$ = new BehaviorSubject<string>('');
  text$ = new BehaviorSubject<string>('');
  serachTerm$: Observable<string>;

  @Output() onTypeahead: EventEmitter<any> = new EventEmitter<any>();
  @Output() onSelect: EventEmitter<any> = new EventEmitter<any>();


  constructor(private differs: KeyValueDiffers) {
    this.differ = this.differs.find({}).create();
  }

  // handleTypeahead = (text$: Observable<string>) =>
  //   text$.pipe(
  //     distinctUntilChanged(),
  //     debounceTime(500),
  //   ).subscribe((value) => {
  //     this.onTypeahead.emit(of(value))
  //   })

  ngOnInit() {

    this.serachTerm$ = this.text$
                           .pipe(
                            distinctUntilChanged(),
                            debounceTime(500),
                            //filter(), //use filter operator if your logic wants to ignore certain string like empty/null
                            tap(s => this.onTypeahead.emit(s))
                           );
  } 

handleSelectItem(item) {
  this.onSelect.emit(item);
}

ngDoCheck() {
    const change = this.differ.diff(this);
    if (change) {
      change.forEachChangedItem(item => {
        if (item.key === 'detectChange'){
          console.log('item changed', item)
          this.text$.next(item.currentValue);          
        }
      });
    }
  }

}

Now, at the bottom of your template put the following line -

<ng-container *ngIf="searchTerm$ | async"></ng-container>

Having this line will keep your component code free form managing the subscription [ie need not to subscribe/unsubscribe]; async pipe will take care of it.

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