How to Extend a Blazor component?
Asked Answered
C

5

8

I'm currently using MudBlazor and really like it.

However, there are things I find myself repeatedly adding to components like CancellationTokens, are customizing the template myself.

Is there a way to completely inherit/extend an existing component?

One option is to make a new component that has an instance of the component I want to modify, and to add all of the parameters to my component and map them back to the original, but I feel there has got to be a better way.

Cylindroid answered 29/7, 2022 at 18:38 Comment(3)
You can add methods through extension, but you can't add properties. You can create your own child components from the MudBlazor components, and then use those. But you probably want to add your stuff higher up the inheritance tree which isn't possible. You can always create your own custom MudBlazor library (if MudBlazor allow it).Stephniestepladder
There is no simple way to extend a component in Blazor - in the sense that you inherit the rendering but get to tinker with the logic. The only way is to wrap the component inside another and use it - similar to decorator pattern. You can do it more efficiently by abstracting the repeating logic into a class and providing the instance of that through a service/creating in the decorator component.Coprolalia
@MayurEkbote thanks. that was actually what i needed to hear. i was trying to force inheritance which wasn't working. composition also seems like the way to go when you want to combine multiple components into 1Jess
R
7

Attribute splatting is really handy for creating wrappers or higher level abstractions around other components:

<MudTextField Variant="@Variant" @attributes="@OtherAttributes"></MudTextField>

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> OtherAttributes { get; set; }

    [Parameter]
    // can even set your own defaults
    public Variant Variant { get; set; } = "Variant.Filled"
}

You can also create your own base components and use @inherits for shared logic and parameters.

Reber answered 29/7, 2022 at 21:41 Comment(1)
Does this support events? @bind-... ?Carrier
T
5

Inheritance

It sounds like you most likely want to @inherit the MudBlazor component in a new component of your own. This essentially allows you to use the MudBlazor component as a base class for your new class/component, avoiding having to redefine all of the parameters that the MudBlazor component already defines.

This can be done in C# or razor. Here's a razor example, OutlinedMudButton.razor, that allows the user to still set all of the usual MudButton parameters, except for the Variant parameter:

@inhertis MudButton

@code {
  // Hide the MudButton's Variant parameter so it can not be changed by users.
  protected new Variant Variant { get; set; }

  protected override void OnInitialized()
  {
    base.OnInitialized();
    base.Variant = Variant.Outlined;
  }
}

This blog post shows a complete example of how to inherit a component to extend it, change default parameter settings, add new UI elements to it, hide/disable parameters of the base component, and potentially change its functionality. If you want to add new UI elements around the component, you can use the following line of code to display the base component that you are inheriting from, placing the new UI elements around it.

@{
    // Display the base component that we are inheriting from.
    base.BuildRenderTree(__builder);
}

Composition

Depending on your use case, you may want to go with composition instead of inheritance, by creating a new component that simply contains an instance of the MudBlazor component that you want to decorate. This may be easier or more appropriate, especially if you want to hide many of the MudBlazor component's parameters so that users are not able to change them. Be aware that if you use attribute splatting as suggested in other answers/comments, the downside is your component will not provide editor intellisense for all of the MudBlazor component's parameters.

Here is another razor example, OutlinedMudButton.razor, that creates a component with an Outlined MudButton where the only thing the user can set is the button Text:

<MudButton Variant="Variant.Outlined">@Text</MudButton>

@code {
  [Parameter]
  public string Text { get; set;}
}
Tawannatawdry answered 9/7, 2023 at 22:37 Comment(1)
Important note about calling base.BuildRenderTree(__builder), you cannot do it in a RenderFragment (i.e. wrapped in another component). For a solution see this answerLello
C
2

You can inherit blazor components like any other non-sealed class

using Microsoft.AspNetCore.Components;
using MudBlazor;

namespace MyMudBlazor;
public partial class MyMudButton : MudButton
{
    [Parameter] public bool IsEnabled
    {
        get { return !base.Disabled; }
        set { base.Disabled = !value; }
    }
}
Christensen answered 11/1, 2023 at 23:8 Comment(0)
F
0

One shortcut I often use is to create a .razor component with no markup. It consumes whatever services I want, adds event handlers, and so on. So instead of inheritance, I have a drag-in sub-component that handles business logic. If it also has methods, then I add an @ref to the Component's markup. Many of my pages look like this:

<FontSizeUtility UserId = @UserId />
<TimerUtility @ref=MyTimer TimerTicker = HandleTimerTick />

@code{
     TimerUtility MyTimer = new();

. . . 
    async Task HandleTimerTick(){
    }

}
Frederic answered 10/7, 2023 at 5:14 Comment(0)
B
0

Just thought I would share my "extended" component. I was looking for a way to extent the MudNumericField component so that a user could enter a percent value but the underlying value would be the decimal value. For example, the user enters 98 but .98 is bound to the variable. I ended up using composition as suggested by @deadlydog

https://try.mudblazor.com/snippet/cEmnbPnOhAGpDEzB

<MudNumericField 
    @bind-Value="DisplayValue" 
    Label="@Label" 
    Variant="@Variant"
    Margin="@Margin" 
    Adornment="@Adornment"
    AdornmentIcon="@AdornmentIcon"
    Clearable="@Clearable"
    HideSpinButtons="@HideSpinButtons"
    Format="@Format"
 />
 
@code {
    private decimal? _value;
    private decimal? _displayValue;
    
    [Parameter] public string Label { get; set; }
    [Parameter] public Variant Variant { get; set; }
    [Parameter] public Margin Margin { get; set; }
    [Parameter] public Adornment Adornment { get; set; } = Adornment.End;
    [Parameter] public string AdornmentIcon { get; set; } = @Icons.Material.Filled.Percent;
    [Parameter] public bool HideSpinButtons { get; set; } = true;
    [Parameter] public bool Clearable { get; set; }
    [Parameter] public string Format { get; set; } = "#.#";
    [Parameter] public EventCallback<decimal?> ValueChanged { get; set; }

    [Parameter] public decimal? Value
    {
        get => _value;
        set
        {
            _value = value;
        }
    }

    
    private decimal? DisplayValue
    {
        get => _displayValue;
        set
        {
            _displayValue = value;
            _value = value / 100;
            ValueChanged.InvokeAsync(_value);
        }
    }

    protected override void OnParametersSet()
    {
        var convertedInput = Value * 100;
        if(convertedInput != DisplayValue) {
            DisplayValue = convertedInput;            
        }
    }
}
Botany answered 30/11, 2023 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.