Search code examples
angularobservableangular8eventemitter

Observable and EventEmitter for pass data parents (Angular)


I have follow structure:

  • Main component (renders items via item-service)
    • Panel component (contains searching components)
      • SerachByTitle component (contains input field for title items)
      • SerachBySomething component (contains something input field for something items)
  • Item service

I want to render items (which fit search-request) within Main-component when user inputs title within SerachByTitle-component.

For this purpose I used EventEmitter: Panel-component, SerachByTitle-component and SerachBySomething-component have decorator @Output.

SerachByTitle and SerachBySomething pass data to Panel-component, and Panel-component forms single object-params, and then passes to Main-component.

I ran into a problem when user inputs title - after each keyup Main-component re-render items. I tried to use combination: debounceTime(timeDelay), distinctUntilChanged(), switchMap(), but it don't help me.

What am I doing wrong?

Sorry for my bad English :(

UPD: For understanding my situation I added example. For main-component I used combination: debounceTime(), distinctUntilChanged(), switchMap(). But delay for search does not exist.


Solution

  • Here is your code (main.component.ts):

    searchChangedHandler(filter){
      this.itemService.searchItems(filter).pipe(
          debounceTime(1300),
          distinctUntilChanged(),
          switchMap((items) => this.listItems = items),
        ).subscribe();
    }
    

    The problem is that you apply debounceTime+distinctUntilChanged in an incorrect place. Here is a simplified diagram of what is happening in your project:

          #1                   #2                       #3                                
    oninput event ---> ItemService.search(term) ----> of(items) ---> ...
    
                               #4                                      #5
    ... ---> pipe(debounceTime(1300)+distinctUntilChanged) ---> this.listItems = items 
    

    Basically you run search on every keystroke, and then apply debounceTime+distinctUntilChanged on the results of the search. Which makes little sense, because you apparently want to limit the number of requests going to your ItemService, so you have to apply debounceTime before calling ItemService

    (You may wonder why you don't see the 1300 delay getting applied even to the results of ItemService.search, and you see changes immediately. The reason is that of(items) on the step #3 on the above diagram creates a new observable that completes immediately, so the delay is not applied).

    So here is the correct way to handle this:

    oninput event ---> pipe(debounceTime(1300)+distinctUntilChanged) ----> ...
    
    ... ----> ItemService.search(term) ----> this.listItems = items 
    
      private searchStream = new Subject<any>();
    
      ngOnInit() {
        this.searchStream
          .pipe(
            debounceTime(1300),
            distinctUntilChanged(),
            switchMap(filter => this.itemService.searchItems(filter))
          )
          .subscribe(items => this.listItems = items);
      }
    
      searchChangedHandler(filter){
        this.searchStream.next(filter);
      }
    

    Here is the corrected project: https://stackblitz.com/edit/angular-iopeep