Reputation: 1895
I am creating a reusable component like this one:
<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>
I would like to disabled the click event when the property isDisabled is true, I tried something like that but it doesn't work.
packages/component/my-button.component.html
<button [disabled]="isDisabled" #myButton>
<ng-content></ng-content>
</button>
packages/component/my-button.component.ts
@ViewChild('uxButton') uxButton: ElementRef;
@Input() isDisabled: boolean = false;
this.myButton.nativeElement.parentNode.removeEventListener('click' , (e) => {
e.stopPropagation();
});
Upvotes: 14
Views: 77109
Reputation: 881
This can be archieved by using a boolean variable to check if some condition is true like so:
<my-button (click)="is_some_condition_true ? null : click($event)"> submit </my-button>
This condition could be for example:
setting the initial value of a variable like editMode, addMode or isDisabled in the component to false and then set this to true when the button is actually disabled.
Ref: how-to-disabled-click-event-or-any-event-if-condition-is-false-true-in-angular
Upvotes: 1
Reputation: 31
I was able to disable the click event of a disabled button in scss. This is what I added
:host {
button {
&:disabled:active {
pointer-events: none;
}
}
}
Upvotes: 3
Reputation: 121
If you want to keep the legacy click event without using output. There is a combined solution based on the previous ones.
my-button.component.html
<button [disabled]="disabled">
<ng-content></ng-content>
</button>
my-button.component.ts
export class MyButtonComponent {
@HostBinding('style.pointer-events') get pEvents(): string {
if (this.disabled) {
return 'none';
}
return 'auto';
}
@Input()
disabled: boolean = false;
constructor() {}
}
parent component where you will call your component e.g. app.component.html
<my-button [disabled]="isDisabled" (click)="onClickFnc()">
<span>Save which not trigger click when button is disabled</span>
</my-button>
Upvotes: 7
Reputation: 182
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:
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.
Upvotes: 1
Reputation: 470
It is also solved by the following CSS:
# This prevents host to get a direct click
:host {
pointer-events: none;
}
# This prevents the span inside the button to "steal" the click from the button
:host /deep/ button span {
pointer-events: none;
}
# Now only the button can get a click
# Button is the only smart enough to stop propagation when needed
button {
pointer-events: auto;
}
And now you don't to pass down the click event manually like in other answers: You have the old (click) event back :D
<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>
In your custom component, you just need to pass down the disabled property:
<button [disabled]="isDisabled" #myButton>
<ng-content></ng-content>
</button>
Also consider the stackblitz modified from another stackoverflow answer.
Upvotes: 17
Reputation: 1605
try like this
<button [disabled]="isDisabled" (click)="btnClick.emit($event)">
<ng-content></ng-content>
</button>
@Input() isDisabled: boolean = false;
@Output() btnClick = new EventEmitter();
Use Output
and By default the button click event won't work if button is disabled. take advantage of it
<my-button [isDisabled]="isDisabled" (btnClick)="click($event)"> submit </my-button>
Upvotes: 14
Reputation: 117
You can check it on (click)
attribute:
<my-button [isDisabled]="isDisabled" (click)="!isDisabled && click($event)"> submit </my-button>
Upvotes: 4
Reputation: 1845
You should use the [disabled]
property as mentioned in the documentation:
<button [disabled]="isDisabled" (click)="disableButton()">Disable button</button>
And then in your code
export class AppComponent {
isDisabled = false;
disableButton() {
this.isDisabled = true;
// your code...
}
}
Check the StackBlitz for the demo.
Upvotes: 0
Reputation: 3698
I think the problem is in your code you have:
<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>
it should be
<my-button [disabled]="isDisabled" (click)="click($event)"> submit </my-button>
Upvotes: 0
Reputation: 2731
We can addeventlistener/remove based on the need using ElementRef/HostListener however the simple fix would be the below.
click(event) {
if (this.isDisabled) {
return;
}
......
}
Upvotes: 0