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()
);
}