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.
