First of all we need to ask ourselves why we want to create a new type of button component, when we already have a native one. It could be something like:
- Take advantage of some Angular helpers such as animations
- Disable the button during execution of some click handler.
- Progress indication.
- ...
If the requirement can be solved with a native button (Solution 0), stick with that. Otherwise, go on.
Two important things we need to know before creating a reusable
button component:
K1 Only a limited subset of HTML elements can be disabled,
https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements. This
means that a click handler is triggered even if the element is disabled.
K2 In Angular, outside events can't be controlled from the inside
inside. https://github.com/angular/angular/issues/12630
Solution 1. Click handler as input
To workaround K2 you could use an @Input
callback instead of an
event binding. Then you have control of it from the inside, and
you even have access to the result of the callback on the inside. It
would look like:
<my-button [myClick]="doIt"> or with arguments <my-button [myClick]="doIt.bind(1)">
@HostListener('click') onClick(event) {
this.myClick();
}
Since, you have complete control over the callback, you can just omit calling
it when it's disabled
.
A problem that cries for this solution, is a button with progress indication. When you have complete control of the callback, the library button could start / stop animations of a progress bar or even block additional clicks by disabling it while in progress. Compare that to the progress buttons in this module
https://github.com/michaeldoye/mat-progress-buttons where you need to
start / stop animations for each instance of the button!
Cons: Non-standard looks. Your library users will be like why is that
callback an input and not an event binding...
Solution 2. CSS
You could try to workaround K1 with CSS pointer-events:none
. It
would work on the surface, blocking user mouse triggered click
events. However, you can still click pragmatically on the
button. myButton.click()
still fires when the button is 'disabled'.
Cons: 'Disabled' elements are still click
able. Probably, not good for
your library users writing automated tests.
Solution 3. Componentize native button
For disable and events to work as expected, you need to apply the
component directly on the HTML button element. In Angular Material it looks
like <button mat-button>
,
https://github.com/angular/components/blob/master/src/material/button/button.ts#L66
And it's quite simple:
@Component({
selector: 'button[my-button]',
template: '<ng-content></ng-content>'
})
And using the it:
<button my-button (click)="doIt()" [disabled]="isDisabled">Save</button>
Now the click event is not fired when my-button
is disabled.
Cons: Native button must be there, and my-button
looks more as if it
were a directive than a component.
Conclusion
I would suggest to go with the solution that best fits the requirement, but not the CSS hack one.