如何扩展 Blazor 组件?

7

我目前正在使用MudBlazor,十分喜欢它。

但有些东西我发现自己不断地向组件中添加,如CancellationTokens,还有自定义模板。

有没有一种方法可以完全继承/扩展现有的组件?

一种选择是创建一个新的组件,该组件具有我想要修改的组件的实例,并将所有参数添加到我的组件中,然后将它们映射回原始组件,但我认为肯定有更好的方法。


您可以通过扩展添加方法,但无法添加属性。 您可以从MudBlazor组件创建自己的子组件,然后使用它们。 但是您可能希望在继承树较高的位置添加自己的内容,这是不可能的。 如果MudBlazor允许,您始终可以创建自己的自定义MudBlazor库。 - MrC aka Shaun Curtis
2
在Blazor中,没有一种简单的方法可以扩展组件 - 即您继承渲染但可以更改逻辑的意义。唯一的方法是在另一个组件内部包装该组件并使用它 - 类似于装饰器模式。通过将重复的逻辑抽象为类并通过服务/在装饰器组件中创建该实例来提供这个类的实例,您可以更有效地执行此操作。 - Mayur Ekbote
1
@MayurEkbote 谢谢您。那正是我需要听到的。我试图强制继承,但并不起作用。当您想将多个组件合并为一个时,组合似乎也是一种可行的方法。 - symbiont
5个回答

4

属性扩展在创建其他组件的包装器或更高级抽象时非常方便:

<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"
}

您还可以创建自己的基础组件,并使用@inherits来共享逻辑和参数。


2

您可以像任何其他非密封类一样继承 Blazor 组件

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; }
    }
}

2

继承

听起来你很可能想要在自己的新组件中@inherit MudBlazor 组件。这实际上允许你将 MudBlazor 组件用作你的新类/组件的基类,避免重新定义 MudBlazor 组件已经定义的所有参数。

这可以在 C# 或者 razor 中完成。下面是一个 razor 示例,OutlinedMudButton.razor,它允许用户仍然设置所有常规的 MudButton 参数,除了 Variant 参数:

@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;
  }
}

这篇博客文章展示了一个完整的例子,说明如何继承一个组件来扩展它,改变默认参数设置,添加新的用户界面元素,隐藏/禁用基础组件的参数,并且可能改变其功能。如果你想在组件周围添加新的用户界面元素,你可以使用以下代码行来显示你正在继承的基础组件,并将新的用户界面元素放置在其周围。
@{
    // Display the base component that we are inheriting from.
    base.BuildRenderTree(__builder);
}

组合

根据您的使用情况,您可能希望使用组合而不是继承,通过创建一个新的组件,其中仅包含您想要装饰的 MudBlazor 组件的实例。这可能更容易或更合适,特别是如果您想隐藏许多 MudBlazor 组件的参数,以防止用户更改它们。请注意,如果您像其他答案/评论中建议的那样使用 属性扩展,缺点是您的组件将无法为所有 MudBlazor 组件的参数提供编辑器智能感知。

这里是另一个 Razor 示例,OutlinedMudButton.razor,它创建了一个带有 Outlined MudButton 的组件,用户只能设置按钮的 Text 属性:

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

@code {
  [Parameter]
  public string Text { get; set;}
}

关于调用base.BuildRenderTree(__builder)的重要说明,您不能在RenderFragment中执行此操作(即包装在另一个组件中)。有关解决方案,请参阅此答案 - Conman_123

0
一个我经常使用的快捷方式是创建一个没有标记的.razor组件。它可以消耗我想要的任何服务,添加事件处理程序等等。所以,与其使用继承,我更喜欢拖入一个处理业务逻辑的子组件。如果它还有方法,那么我会在组件的标记中添加一个@ref。我的许多页面都是这样的。
<FontSizeUtility UserId = @UserId />
<TimerUtility @ref=MyTimer TimerTicker = HandleTimerTick />

@code{
     TimerUtility MyTimer = new();

. . . 
    async Task HandleTimerTick(){
    }

}

0
只是想分享一下我的“扩展”组件。我正在寻找一种方法来扩展MudNumericField组件,以便用户可以输入百分比值,但底层值将是小数值。例如,用户输入98,但.98绑定到变量。最后,我采用了@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;            
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接