将值类型传递给 Blazor 组件

时间:2021-06-21 11:08:25

标签: blazor blazor-server-side blazor-webassembly

我正在创建一个类似于 DataTables 的 Blazor 组件,并且我还想实现通用过滤器。为此,我为数字创建了 2 个组件:一个是整数,另一个是浮点数。

从主要组件,我想传递数字类型。

<NumberInput TNumber="short"
    Value="@FilterRule.FilterValue"
    OnValueChangedEvent="@FilterRule.UpdateFilterValue"
    IncludeApply="IncludeApply()"
    IsApplied="@FilterRule.IsApplied"
    AppliedText="@FilterRule.FilterValue!.ToString()"
    ApplyFilterEvent="(e) => ApplyFilter(FilterRule.Guid, true)"
    UnApplyFilterEvent="(e) => UnApplyFilter(FilterRule.Guid, true)"
    Attributes="Attributes"
    ApplyButtonCssClass="@ApplyButtonCssClass"
    InputCssClass="@InputCssClass"
    MaxWidth="MaxWidth" />

例如,TNumber 可以有 shortintlong 等。这是因为我有一个函数可以为我提供每种类型数字的最小值和最大值。

public static Tuple<T, T> GetMinMaxValue<T>()
{
    object obj1 = (object)default(T);
    object obj2 = (object)default(T);

    if (obj1 == null || obj2 == null)
        return (Tuple<T, T>)null;

    switch (Type.GetTypeCode(typeof(T)))
    {
        case TypeCode.Int16:
            obj1 = (object)char.MinValue;
            obj2 = (object)char.MaxValue;
            break;
        case TypeCode.SByte:
            obj1 = (object)sbyte.MinValue;
            obj2 = (object)sbyte.MaxValue;
            break;
        // and so on...
    }
}

NumberInput 具有以下代码:

<input class="@DefaultClass form-control-sm @InputCssClass" type="number" value="@Value"
       style="@(MaxWidth != 0 ? string.Format("max-width: {0}px", (object) this.MaxWidth) : "")"
       @onchange="@UpdateValue" step="any">

@code {
    [Parameter] public long Value { get; set; }
    [Parameter] public EventCallback<ValueChangedEventArgs> OnValueChangedEvent { get; set; }
    [Parameter] public string DefaultClass { get; set; } = "form-control";
    [Parameter] public bool IncludeApply { get; set; } = false;
    [Parameter] public bool IsApplied { get; set; } = false;
    [Parameter] public string AppliedText { get; set; } = "";
    [Parameter] public string InputCssClass { get; set; } = "";
    [Parameter] public string ApplyButtonCssClass { get; set; } = "";
    [Parameter] public Dictionary<string, object> Attributes { get; set; }
    [Parameter] public int MaxWidth { get; set; }
    [Parameter] public EventCallback<MouseEventArgs> ApplyFilterEvent { get; set; }
    [Parameter] public EventCallback<MouseEventArgs> UnApplyFilterEvent { get; set; }

    private async Task UpdateValue(ChangeEventArgs args)
    {
        Value = Convert.ToInt32(args.Value.ToString());
        await this.OnValueChangedEvent.InvokeAsync(new ValueChangedEventArgs((object)this.Value));
    }
}

那么,问题是:如何将我想要使用的变量类型作为参数传递?

1 个答案:

答案 0 :(得分:0)

@typeparam 指令


正如注释中暗示的https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0,解决方案是在NumberInput 组件中添加一个类型参数。在 NumberInput 组件中,添加 @typeparam Razor 指令:

@typeparam TNumber 

这不过是变相的泛型类型参数。 您现在可以在 NumberInput 组件中的任何位置使用此参数,就像在 C# 类型(如类或结构)中使用泛型参数一样。 大多数情况下,可以推断类型参数的类型,并且显式设置它们是多余的。但是,如果无法推断类型,则可以明确指定:

<NumberInput 
    TNumber="int"                                       @* Explicitly passing type *@
    Value="@FilterRule.FilterValue"
    OnValueChangedEvent="@FilterRule.UpdateFilterValue"
    IncludeApply="IncludeApply()"
    IsApplied="@FilterRule.IsApplied"
    AppliedText="@FilterRule.FilterValue!.ToString()"
    ApplyFilterEvent="(e) => ApplyFilter(FilterRule.Guid, true)"
    UnApplyFilterEvent="(e) => UnApplyFilter(FilterRule.Guid, true)"
    Attributes="Attributes"
    ApplyButtonCssClass="@ApplyButtonCssClass"
    InputCssClass="@InputCssClass"
    MaxWidth="MaxWidth" />

避免覆盖参数


另外,正如 Brian Parker 指出的,你不应该覆盖参数值,因为如果容器组件调用 StateHasChanged(),子组件的所有参数都将重置为其原始值,状态将迷路了。有关详细信息,请参阅 Mister Magoo。要安全地维护状态,请先将参数值复制到私有字段,在 OnInitializedOnInitializedAsync 中:

private long _value;
protected override void OnInitialized()
{
        _value = Value;
}

最后的NumberInput


最后,NumberInput 看起来像这样:

@typeparam TNumber

<input class="@DefaultClass form-control-sm @InputCssClass" 
       type="number" 
       value="@Value"
       style="@(MaxWidth != 0 ? string.Format("max-width: {0}px", (object) this.MaxWidth) : "")"
       @onchange="@UpdateValue" 
       step="any" />

@code {

    private long _value;

    [Parameter] public long Value { get; set; }
    [Parameter] public EventCallback<ValueChangedEventArgs> OnValueChangedEvent { get; set; }
    [Parameter] public string DefaultClass { get; set; } = "form-control";
    [Parameter] public bool IncludeApply { get; set; } = false;
    [Parameter] public bool IsApplied { get; set; } = false;
    [Parameter] public string AppliedText { get; set; } = "";
    [Parameter] public string InputCssClass { get; set; } = "";
    [Parameter] public string ApplyButtonCssClass { get; set; } = "";
    [Parameter] public Dictionary<string, object> Attributes { get; set; }
    [Parameter] public int MaxWidth { get; set; }
    [Parameter] public EventCallback<MouseEventArgs> ApplyFilterEvent { get; set; }
    [Parameter] public EventCallback<MouseEventArgs> UnApplyFilterEvent { get; set; }

    private async Task UpdateValue(ChangeEventArgs args)
    {
        _value = Convert.ToInt32(args.Value.ToString());
        await this.OnValueChangedEvent.InvokeAsync(new ValueChangedEventArgs((object)this.Value));
    }

    protected override void OnInitialized()
    {
        _value = Value;
    }
}