在 Blazor 中将通用 TItem 与“onchange”事件一起使用

时间:2021-06-27 10:58:33

标签: c# .net blazor blazor-webassembly

我在 Blazor WebAssembly 中有一个通用选择器组件,它在选择选项时根据 onchange 事件调用函数。问题是 ChangeEventArgs 是对象类型,我无法将它转换为 TItem 而不会收到错误 Specified cast is not valid。有没有解决的办法?如何让事件在这样的通用组件中正常工作?

@typeparam TItem

<select class="form-control selectpicker" value="@Selected" @onchange="Update">
    @foreach (var item in Options)
    {
        <option value="@item">@item</option>
    }
</select>

@code {

    [Parameter] public List<TItem> Options { get; set; }

    [Parameter] public TItem Selected { get; set; }

    [Parameter] public EventCallback<TItem> SelectedChanged { get; set; }

    private async Task Update(ChangeEventArgs e)
    {
        await SelectedChanged.InvokeAsync((TItem) e.Value); // e.Value is type object, cast to TItem fails
    }
}

2 个答案:

答案 0 :(得分:2)

如果您将复杂对象列表传递给选项,则这些对象无法直接呈现为 HTML,然后在选择后映射回源复杂对象。

问题演示

运行以下命令并更改为第二个选择器(字符串)上的第 2 项,您将不会收到任何错误。在第 1 次选择时更改为第 2 项(OptionItem - 复杂),您将收到错误消息:

GenericSelector.razor

@typeparam TItem

<select class="form-control selectpicker" value="@Selected" @onchange="Update">
    @foreach (var item in Options)
    {
        <option value="@item">@item</option>
    }
</select>

@code {

    [Parameter] public List<TItem> Options { get; set; }

    [Parameter] public TItem Selected { get; set; }

    [Parameter] public EventCallback<TItem> SelectedChanged { get; set; }

    private async Task Update(ChangeEventArgs e)
    {
        await SelectedChanged.InvokeAsync((TItem)e.Value); 
    }
}

GenericSelectorPage.razor

@page "/genericselector"
@using NMW.Blazor.Components.GenericSelector

<GenericSelector TItem="OptionItem" Options=@Options />
<GenericSelector TItem="string" Options=@StringOptions />

@code {        
    List<OptionItem> Options { get; set; }
    List<string> StringOptions { get; set; }


    protected override void OnInitialized()
    {
        Options = new List<OptionItem>
        {
            new OptionItem { Id = "1", Text = "One"},
            new OptionItem { Id = "2", Text = "Two"}
        };

        StringOptions = new List<string>
        {
            "One",
            "Two"
        };

        base.OnInitialized();
    }


    public class OptionItem
    {
        public string Id { get; set; }
        public string Text { get; set; }
    }

}

一种可能的解决方案

在您的选择组件中声明接口:

public interface ISelectable
{
    string Id { get; }
    string Text { get; }
}

添加部分代码隐藏文件,以便您可以约束类型参数:

public partial class GenericSelector<TItem> where TItem : ISelectable
{ }

在您的复杂对象上实现 ISelectable:

public class ComplexOptionItem : ISelectable
{
    // ISelectable Implementation
    public string Id => MyId;
    public string Text => MyText;

    // Original Properties
    public string MyId { get; set; }
    public string MyText { get; set; }
}

如果您不想更改原始复杂对象以实现 ISelectable,则声明一个从 ComplexOptionItem 继承并实现 ISelectable 的 ComplexOptionView 类

public class ComplexOptionView : ComplexOptionItem, ISelectable
{
    // ISelectable Implementation
    public string Id => MyId;
    public string Text => MyText;
}

更新您的通用选择器组件以使用 ISelectable 来获取用于渲染的显示属性,然后从传入的参数中检索复杂对象:

@typeparam TItem

<select class="form-control selectpicker" value="@Selected" @onchange="Update">
    @foreach (ISelectable item in Options)
    {
        <option value="@item.Id">@item.Text</option>
    }
</select>

@code {

    [Parameter] public List<TItem> Options { get; set; }

    [Parameter] public TItem Selected { get; set; }

    [Parameter] public EventCallback<TItem> SelectedChanged { get; set; }

    private async Task Update(ChangeEventArgs e)
    {
        // Retrieve original option based on Id
        TItem selected = Options.Single(i => i.Id == e.Value);
        // Raise the SelectedChanged event using the source object
        await SelectedChanged.InvokeAsync(selected);
    }
}

答案 1 :(得分:1)

您可以使用值为选择器指定 Item

<MySelector Options="availableTags" SelectedChanged="(val) => { selectedTag = availableTags.Single(x => x.Id == new Guid((string)val)); }">
<Item>
    <option  value="@context.Id">@context.Name</option>
</Item>    
</MySelector>

现在,从 SelectedChanged 返回的值是 object 类型,但实际上是 string,可以转换为 Guid(因为值被指定为 { {1}},属于 context.Id 类型)。

Guid 是带有 availableTagsTagName 类型列表:

Id

还要更改您的选择器以接受 public class Tag { public Guid Id { get; set; } = Guid.NewGuid(); public string Name { get; set; } } 并在更改时返回 Item,也不需要 object 参数:

Selected