I have written an Angular Directive that disables a button after it has been clicked, to prevent double posting / saving of data. This works fine in most of our use cases. However, in some situation we want to re-enable the button. For example: the button-click triggers a form submit, but the server-side validation fails. We want to allow the user to submit again, so we need to re-enable the button.
My solution uses an EventEmitter
that is owned by the 'host' Component. This EventEmitter
is passed on to the button's Directive. This Directive then subscribes to it. Whenever the host wants to instruct the button to re-enable, is emits an event. The Directive will take care of enabling the button.
As an alternative, one could use the disabled
property of the button to control its state. However, I would like to reserve that property for other logic that is part of the host Component (e.g. are all required fields correctly filled, etc.). I would like to keep the code for the double posting safeguard contained to the Directive as much as possible.
I'd like to learn if there might be a cleaner way to accomplish what I'm after. Here's what I do now:
The button as used in the host Component template
<button [appDisableAfterClick]="reenableButton"
(click)="doStuff()">My button</button>
Inside the host Component
private reenableButton = new EventEmitter<boolean>();
doStuff() {
// Make some external call.
// In case of an error, re-enable the button:
this.reenableButton.emit();
}
The DisableAfterClick
Directive
Several null checks and unsubscribe calls omitted for brevity.
@Directive({
selector: '[appDisableAfterClick]'
})
export class DisableAfterClickDirective implements OnChanges, OnDestroy {
@Input('appDisableAfterClick') reenableButton: EventEmitter<boolean>;
ngOnChanges(changes: SimpleChanges) {
this.reenableButton = changes.reenableButton.currentValue;
this.reenableButton.subscribe(_ => {
(<HTMLButtonElement>this.el.nativeElement).disabled = false;
});
}
@HostListener('click')
onClick() {
(<HTMLButtonElement>this.el.nativeElement).disabled = true;
}
}