Search code examples
blazorblazor-webassemblyrazor-components

Blazor - What is the correct way to extend another component?


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.


Solution

  • 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.