TL;DR: I have created a button component in Angular and I want to utilise the best practice reactive behaviour in this button firing behaviour in whichever component decides to use it. I don't know what that best practice is.
I have created a component which renders a button and needs to invoke an action in the parent component. What I have currently is the parent component with this in the template:
<my-button (clicked)="doSomething()"></my-button>
The parent component code has this function being fired:
doSomething() {
// ..stuff happens
}
The problem is that the myBtn
component is currently listening to a click event on a button
element like this:
<button type="button" (click)="doClick()"></button>
And this function is used to fire the event itself (so here is the full button component code):
import { Component, EventEmitter, Input, Output } from "@angular/core";
@Component({
selector: "my-button",
templateUrl: "./my-button.component.html",
styleUrls: ["./my-button.component.css"]
})
export class MyButtonComponent {
constructor() {}
@Input() someCondition: boolean;
@Output() clicked = new EventEmitter();
doClick() {
if (!this.someCondition) {
this.clicked.emit(); // NOT REACTIVE???
}
}
}
I recognise that the doClick
logic is not the best practice, but I'm not sure what the best practice is.
CodePudding user response:
The best code I have come up with so far is this:
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: "my-button",
templateUrl: "./my-button.component.html",
styleUrls: ["./my-button.component.css"]
})
export class MyButtonComponent {
destroy$ = new Subject();
constructor() {}
@ViewChild("refreshBtn", { 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$))
.subscribe(([_, refresh]) => !refresh && this.clicked.emit());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
This works and, so far, is the "most reactive" code I can come up with.