typescript - Hvordan forhindre klikk på egendefinert knappkomponent i Angular 4


tl;dr - Hvordan får jeg click()-håndtereren på myButton-komponenten til å respektere knappens deaktivert-tilstand?

Jeg har en tilpasset knapp-komponent som brukes for å komponentisere knapper med lignende utseende og følelse, inkludert tekst og FA-ikoner. Knapp-komponenten får visse input, inkludert teksten sin og en flagg for om den er deaktivert eller ikke. Når den er deaktivert, vises den korrekt (dvs. nedtonet og med ikke-røyk pekeren), men dens click()-håndterer mottar alltid klikket.

Jeg tror jeg vet hvorfor dette skjer, men jeg vet ikke hvordan jeg skal fikse det. Jeg har prøvd å erklære en Output() click=new EventEmitter<any>() på knapp-komponenten i håp om at Angular på en eller annen måte ville koble den riktig, men det fungerte ikke.

Forbrukeren av knappen min ville gjort det med noe slikt:

//foo.component.html
...
<my-button
  (click)="saveWidget()"
  [disabled]="shouldBeDisabled"
  type="{{buttonTypes.save}}">
</myButton>
...

//foo.component.ts
import { ButtonTypes } from './myButton';

export class FooComponent implements OnInit {
  buttonTypes = ButtonTypes;

  saveWidget() {
    //do some saving
  }

  get shouldBeDisabled() {
    return true; //Normally a real test in here
  }
  ...
}

Mitt problem er at når denne knappen brukes, blir klikkbehandleren (dvs. saveWidget() på foo.component) kalt uansett my-button's deaktiverte tilstand.

Knappkomponenten ser omtrent slik ut:

export enum ButtonTypes {
  add,
  cancel,
  save,
}

@Component({
  selector: 'my-button',
  template: `<button [type]="tagType"
                [disabled]="disabled"
                [ngClass]="btnClasses">
                  <i *ngIf="hasLeft" class="fa" [ngClass]="iconCls" aria-hidden="true"></i>
                  <span class="hidden-xs hidden-sm">{{text}}</span>
                  <i *ngIf="hasRight" class="fa" [ngClass]="iconCls" aria-hidden="true"></i>
              </button>`
})
export class ButtonComponent implements OnInit {
  @Input() disabled = false;
  @Input('type') btnType: ButtonTypes;
  @Input() color?: ButtonColor;
  @Input() size?: ButtonSize;
  ...
}

Svar #1

Du kan bruke @HostBinding('class') og :host-velgere for å deaktivere klikk via pointer-events: none basert på din shouldBeDisabled-egenskap:

//foo.component.scss

:host(.disabled) {
  // Disable click event.
  pointer-events: none;
}

:host(.enabled) {
  // No customizations.
}

//foo.component.html
...
<my-button
  (click)="saveWidget()"
  [disabled]="shouldBeDisabled"
  type="{{buttonTypes.save}}">
</myButton>
...

//foo.component.ts
import { ButtonTypes } from './myButton';

export class FooComponent implements OnInit {

  @HostBinding('class')
  get hostClass(): string {
    return this.shouldBeDisabled ? 'disabled' : 'enabled';
  }

  get shouldBeDisabled() {
    return true; //Normally a real test in here
  }
  ...
}

Jeg syntes Learning Angular: Conditionally add styles to an element var veldig nyttig for å jobbe gjennom et lignende problem.

Svar #2

Den indre disabled-tilstanden til ButtonComponent vil IKKE forhindre at den ytre (click) knyttet til komponenten blir utløst. Du kan derimot forhindre at en (click) skjer innenfor ButtonComponent basert på @Input disabled: boolean. Å bruke dette i kombinasjon med @Output() kan tillate deg å lytte etter klikkhendelser på barnets aktivert-knapp i parent-komponenten. Dette er på samme linje som at parent lytter etter barnehendelser i Angular-dokumentasjonen.

Denne løsningen legger effektivt bare til noen få ekstra kodelinjer og hjelper til med å sikre at den ytre håndtereren ikke kjøres med mindre knappen er aktivert og klikket.

Knappkomponent:

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-button',
  template: `
    <button [type]="type" [disabled]="disabled" (click)="handleClick()">
      <ng-content></ng-content>
    </button>
  `
})
export class ButtonComponent  {
  @Input() disabled: boolean;
  @Input() type: string;
  @Output() clicked = new EventEmitter<boolean>();

  // will not execute if the button is disabled
  handleClick() {
    console.log('ButtonComponent: clicked');
    this.clicked.emit(true);
  }
}

Foreldrekomponent:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <app-button
        [disabled]="shouldBeDisabled"
        [type]="'button'"
        (clicked)="saveWidget($event)">ENABLED</app-button>
    <app-button
        [disabled]="shouldBeDisabled"
        [type]="'button'"
        (clicked)="saveWidget($event)">DISABLED</app-button>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  get shouldBeDisabled() { return true; }
  get shouldNotBeDisabled() { return false; }

  saveWidget(emittedValue: boolean) {
    console.log(emittedValue);
    console.log('saveWidget()');
  }
}

Du kan sende hva som helst du trenger til forelderen. I dette eksemplet er det en boolean, men det kan være noe for å identifisere widgeten eller hva som helst du trenger i eksemplet ditt.

Her er en StackBlitz som viser funksjonaliteten i aksjon.

Relaterte artikler