Home > Blockchain >  Custom Angular button component needs to fire event
Custom Angular button component needs to fire event

Time:07-17

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.

  • Related