'Is there a way to make disabled HTML elements not register Blazor events?

I have found a repetitive pattern in Blazor components when needing to disable an HTML element.

Blazor provides functionality with the HTML disabled attribute, to which you can pass some Razor.

With the example of a single button, we can do:

<button type="button" @onclick="DoSomething" disabled="@buttonIsDisabled">Click me</button>

Blazor will render the button including a disabled attribute when buttonIsDisabled is true. This helps when using a CSS class to alter the styling of the button when disabled as well.

However, this is not very safe since a user can edit the HTML document quickly to remove the disabled attribute from the HTML element. The on-click event would remain and allow the DoSomething method to execute anyways.

To avoid things being bypassed, I use a manual approach instead:

if (buttonIsDisabled)
{
    <button disabled type="button" />
}
else
{
    <button @onclick="DoSomething" type="button" />
}

There is an enabled version of the button with an onclick handler, and another without it for safety (disabled).

The problem is that in more complex scenarios, is not a simple button which has to be rendered, but multiple elements with multiple handlers as well. This ends up in a pattern of repetitive/duplicated HTML code.

Is there a better approach or another way to disable elements so that Blazor does not register the event handlers such the onclick event?

For example if Blazor knew that an element should render the disabled attribute should not trigger any Blazor event.

Edit: This question is not intended to find a solution for the case of a disabling/enabling a simple button and controlling it's event handler, but to the pattern I explain above. Imagine having not only disabled buttons, but also inputs, checkboxes, toggles, etc; they could all be also linked to multiple event handlers, each.

A not-supported idea: As Blazor computes the disabled attribute to render it or not, it could also determine to not include the event handlers for the element when it should be disabled in the next rendering.



Solution 1:[1]

Applying one of the most basic good coding practices - DRY - Don't Repeat Yourself, here's a simple "button" component:

@if (Show)
{
    @if (this.Disabled)
    {
        <button type="button" disabled @attributes=this.UserAttributes>@ChildContent</button>
    }
    else
    {
        <button type="button" @onclick="this.OnClick" @attributes=this.UserAttributes>@ChildContent</button>
    }
}

@code {
    [Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> UserAttributes { get; set; } = new Dictionary<string, object>();
    [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
    [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; }
    [Parameter] public bool Disabled { get; set; }
    [Parameter] public bool Show { get; set; } = true;
}

Which you can then use like this:

@page "/"
<h3>Test</h3>

<MyButton class="btn btn-dark" Disabled=this.disabled Show=this.show OnClick=this.OnClick>Click me</MyButton>
<MyButton class="btn btn-primary" OnClick=this.DisableMe>Disable me</MyButton>
<MyButton class="btn btn-outline-info" OnClick=this.ShowMe>Show me</MyButton>

@code {
    private bool disabled;
    private bool show = true;

    private void DisableMe(MouseEventArgs e)
        => disabled = !disabled;

    private void ShowMe(MouseEventArgs e)
        => show = !show;

    private void OnClick(MouseEventArgs e)
    {
        // Do something
    }
}

You can use this same pattern for your more complex stuff. Personally, I have a base component (directly implemented as a class) that implements the basics and then derived classes for the more complex stuff

[Polite] Please don't take this the wrong way, but I'm amazed how often DRY goes out the window when writing Razor code!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1