4

I'm using MudBlazor component library. In order to show loading on form buttons, the documentation guides like this:

<MudButton Disabled="@_processing" OnClick="ProcessSomething" Variant="Variant.Filled" Color="Color.Primary">
    @if (_processing)
    {
        <MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
        <MudText Class="ms-2">Processing</MudText>
    }
    else
    {
        <MudText>Click me</MudText>
    }
</MudButton>

Now since I'm doing this a lot, I wanted to wrap this logic inside another component.

The following component does not do the job:

@inherits MudButton

@code {
    bool _loading;

    [Parameter]
    public bool Loading 
    {
        get => _loading;
        set
        {
            _loading = value;
            Disabled = value;
        }
    }

    [Parameter]
    public new RenderFragment ChildContent 
    {
        get => base.ChildContent;
        set => base.ChildContent = ExtendContent(value);
    }

    private RenderFragment ExtendContent(RenderFragment baseContent) => __builder =>
    {
        if (Loading)
        {
            <MudProgressCircular Class="ms-n2" Size="Size.Small" Indeterminate="true" />
        }
        @baseContent
    };
}

I get this error:

The type '<my_component>' declares more than one parameter matching the name 'childcontent'. Parameter names are case-insensitive and must be unique.

Parsa99
  • 307
  • 1
  • 13

3 Answers3

3

Create a component for the loading content:

ButtonLoadingContent.razor

@using MudBlazor
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
<MudText Class="ms-2">Processing</MudText>

BaseLoadingMudButton.cs

using MudBlazor;

public class BaseLoadingMudButton<TComponent> : MudButton
    where TComponent : ComponentBase
{
    protected override void OnParametersSet()
    {
        Disabled = Loading;

        if (Loading is true)
        {
            ChildContent = builder =>
            {
                builder.OpenComponent<TComponent>(sequence: 1);
                builder.CloseComponent();
            };
        }
        base.OnParametersSet();
    }

    [Parameter]
    public bool Loading { get; set; }
}

LoadingMudButton.cs

public class LoadingMudButton : BaseLoadingMudButton<ButtonLoadingContent> { }

Usage

<LoadingMudButton Loading=_processing OnClick="ProcessSomething" Variant="Variant.Filled" Color="Color.Primary">
    <MudText>Click me</MudText>
</LoadingMudButton>

enter image description here enter image description here

Why does it work? I didn't use .razor files for the inheritance. The base component does not have to use generics I included this to make it more flexible and as an example.

Brian Parker
  • 11,946
  • 2
  • 31
  • 41
0

MudButton already has ChildContent Parameter, you don't need to inherit it, Just use it inside your new component, and redefine the parameters you need to pass them to the inner components

buga
  • 852
  • 9
  • 21
0

MudBlazor's component library inherits from ComponentBase which isn't really designed for Razor inheritance. You're trying to replace the ChildContent with your own markup.

You need to lift the markup stuff from the base component, hope there's no privates being used, and copy it into the child markup.

Here's my inherited component:

@inherits MudButton
@using MudBlazor.Extensions

<MudElement @bind-Ref="@_elementReference"
            HtmlTag="@HtmlTag"
            Class="@Classname"
            Style="@Style"
            @attributes="UserAttributes"
            @onclick="OnClickHandler"
            type="@ButtonType.ToDescriptionString()"
            href="@Href"
            target="@Target"
            rel="@(Target=="_blank"?"noopener":null)"
            disabled="@Disabled">

    <span class="mud-button-label">
        @if (!string.IsNullOrWhiteSpace(StartIcon))
        {
            <span class="@StartIconClass">
                <MudIcon Icon="@StartIcon" Size="@Size" Color="@IconColor" />
            </span>
        }

        @if (this.Loading)
        {
            <MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
            <MudText Class="ms-2">Processing</MudText>
        }
        else
        {
            <MudText>@ChildContent</MudText>
        }

        @if (!string.IsNullOrWhiteSpace(@EndIcon))
        {
            <span class="@EndIconClass">
                <MudIcon Icon="@EndIcon" Size="@Size" Color="@IconColor" />
            </span>
        }
    </span>

</MudElement>

@code {
    [Parameter] public bool Loading { get; set; }
}

The code comes from the MubBlazor Github repo here - https://github.com/MudBlazor/MudBlazor/blob/dev/src/MudBlazor/Components/Button/MudButton.razor.

My Demo Page:

@page "/"

<PageTitle>Index</PageTitle>

<MudText Typo="Typo.h3" GutterBottom="true">Hello, world!</MudText>
<MudText Class="mb-8">Welcome to your new app, powered by MudBlazor!</MudText>
<MudAlert Severity="Severity.Normal">You can find documentation and examples on our website here: <MudLink Href="https://mudblazor.com" Typo="Typo.body2" Color="Color.Inherit"><b>www.mudblazor.com</b></MudLink></MudAlert>

<MudText class="mt-6">

    <MudButton Disabled="@_processing" OnClick="ProcessSomething" Variant="Variant.Filled" Color="Color.Primary">
        @if (_processing)
        {
            <MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
            <MudText Class="ms-2">Processing</MudText>
        }
        else
        {
            <MudText>Click me</MudText>
        }
    </MudButton>

    <MyButton Loading=_processing Disabled="@_processing" OnClick="ProcessSomething" Variant="Variant.Filled" Color="Color.Primary">
        Hello
    </MyButton>

</MudText>
@code {
    private bool _processing;

    private async Task ProcessSomething()
    {
        _processing = true;
        await Task.Delay(5000);
        _processing = false;
    }
}

enter image description here

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
  • I had this in mind, but the problem is that every time I update the MudBlazor nuget I need to check if there is any change in the original component. This is not a safe way to extend, it may cause problem on library updates. – Parsa99 Dec 04 '22 at 12:40
  • 1
    I agree with you. There's no guaranteed safe way to extend third party components. The answer to your question is: there's no correct way. Either use as is or develop your own. Almost every component out there inherits from `ComponentBase` which is another story! – MrC aka Shaun Curtis Dec 04 '22 at 14:33