Home > database >  Why is observer not dumping to console?
Why is observer not dumping to console?

Time:07-14

I have a component which listens for click events from an HTML button and takes an observable from it's parent. When the button is clicked, it needs to check the state of the observable, checking for false, and emitting an event. I am trying to debug the reactive code here, but the console.log() is not printing to the console.

import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from "@angular/core";
import { combineLatest, fromEvent, Observable, Subject } from "rxjs";
import { debounceTime, distinctUntilChanged, filter, map, takeUntil, withLatestFrom } from 'rxjs/operators';

@Component({
  selector: "spinner-button",
  templateUrl: "./spinner-button.component.html",
  styleUrls: ["./spinner-button.component.css"]
})
export class SpinnerButtonComponent {
  destroy$ = new Subject();

  constructor() {}

  @ViewChild("clicker", { static: true }) clickerBtn: ElementRef;
  
  @Input() refreshState$: Observable<boolean>;

  @Input() label: string;
  @Input() refreshLabel: string;

  @Output() clicked = new EventEmitter();

  ngAfterViewInit() {
    fromEvent(this.clickerBtn.nativeElement, 'click')
      .pipe(takeUntil(this.destroy$), debounceTime(300), withLatestFrom(this.refreshState$), map(([a, b]) => {
        console.log('bump',a,b); // DOES NOT PRINT
        if (!b) {
          this.clicked.emit();
        }
      }))
      .subscribe(c => this.clicked.emit());
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Why does this not print to the console?

EDIT

The button is added to the parent like this:

<spinner-button [refreshState$]="refreshing$"></spinner-button>

And refreshing$ is this:

refreshing$: Observable<boolean>;

This value is assigned a value by this:

this.refreshing$ = this.priceStore$.pipe(map(data => data.refreshing));

CodePudding user response:

I don't see where the problem is, you need to debug yourself, but a couple of things worth mentioning.

withLatestFrom

Combines the source Observable with other Observables to create an Observable whose values are calculated from the latest values of each, only when the source emits.

Verify that you are actually pressing the button, and that no CSS or something else isn't blocking it. If your source (clickerBtn) doesn't emit, none of the code executes. Try adding a (click)="" listener on button to verify this?

The second thing that was mentioned in the comments was to check this.refreshState$, has something.

Otherwise your code is fine.

<button #clicker>test me</button> 
Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit, OnInit {
  destroy$ = new Subject<void>();

  constructor() {}

  @ViewChild('clicker', { static: true }) clickerBtn: ElementRef;
  @Input() refreshState$: Observable<boolean>;
  @Input() label: string;
  @Input() refreshLabel: string;
  @Output() clicked = new EventEmitter();

  ngOnInit() {
    this.refreshState$ = of(false); // <--- try change this
  }

  ngAfterViewInit() {
    fromEvent(this.clickerBtn.nativeElement, 'click')
      .pipe(
        takeUntil(this.destroy$),
        debounceTime(300),
        withLatestFrom(this.refreshState$),
        map(([a, b]) => {
          console.log('bump', a, b); // DOES NOT PRINT
          if (!b) {
            console.log('emitting that was pressed from pipe side effect');
            this.clicked.emit();
          }
        })
      )

      .subscribe((c) => {
        console.log('emitting that was pressed in subscribe');
        this.clicked.emit();
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Stackblitz: https://stackblitz.com/edit/angular-ivy-zhar6z?file=src/app/app.component.ts

CodePudding user response:

AFAIK, withLatestFrom doesn't emit if the second observable emits after the source observable. You might actually be looking for RxJS combineLatest function here.

Also RxJS map operator is used to transform the emission from and observable and return a data. Since you aren't returning anything from it now, you could use tap operator instead.

Try the following

import { combineLatest } from 'rxjs';
import { tab } from 'rxjs/operators';

ngAfterViewInit() {
  combineLatest([
    fromEvent(this.clickerBtn.nativeElement, 'click'),
    this.refreshState$
  ]).pipe(
    tap(([a, b]) => {
      console.log('bump',a,b); // DOES NOT PRINT
      if (!b) {
        this.clicked.emit();
      }
    })
  ).subscribe(c => 
    this.clicked.emit()
  );
}
  • Related