如何制作一个可以接收不同对象列表作为参数的通用 Blazor 组件?

Posted

技术标签:

【中文标题】如何制作一个可以接收不同对象列表作为参数的通用 Blazor 组件?【英文标题】:How can one make a generic Blazor Component which can take in varying lists of objects as parameters? 【发布时间】:2020-04-18 23:19:00 【问题描述】:
<label for="client-list" class="label" style="display:inline">Client</label> <i class="fas fa-user-plus"></i> <br />
<input class="input field" value="@(GetClientName(@ClientID))" @oninput="@ClientSearchUpdated"/>
@if (AvailableClients != null)

    <ul>
        @foreach (var client in AvailableClients)
        
            <li id="@client.Id" @onclick="@(e => SetClientID(e, client.Id))">@client.FullName</li>
        
    </ul>

我希望能够采用上述标记/代码并将其转换为可重用组件,该组件将创建一个输入字段,当输入该字段时,将根据输入的内容显示结果列表。这些结果将是传入的列表类型。例如:如果传入List&lt;Person&gt;,它将在数据库中搜索与用户输入的搜索词相匹配的人。在通用版本中,包含对象的列表searched 不会是 AvailableClients,当然也不会是获取/更新特定于客户的信息的函数。

最后,我的目标是能够将上面的代码片段替换为:

<SearchableDropdown DropdownItems="AvailableClients"></SearchableDropdown>

(搜索的字段当前由当前每个DataAccessObjects中使用的sproc确定)

我在尝试开发这样一个泛型组件时遇到的问题是我对泛型并不是很熟悉(我了解基本概念,使用泛型的语法是什么等等,但我没有没有创建很多通用代码),尤其是在将该概念与 Blazor 集成时。

到目前为止我尝试过的是:

    使用继承来接受通用List&lt;ISearchableDropdownItem&gt;,我系统中的对象将实现这个接口,它有两个成员:IDDropdownDisplayInfo,这将允许下拉列表发送有关哪个项目的信息点击,并为每个项目在搜索结果中显示每个项目。

这种方法的问题是我必须为 DataAccess 层和我在 Blazor 应用程序中创建的服务创建接口。这是一个冗长的级联问题,会导致我陷入界面创建兔子洞。我什至不能 100% 确定这个解决方案最终能否奏效。

    使用@typeparam TDropdownItem 指令允许在整个组件中使用的类型是传入的类型。

这样做的明显问题是,它仍然会让使用 SearchableDropdown 组件为 RenderFragment 提供适当降价的人承担很多责任,而且仍然存在泛型问题,“GetObjectName(ObjectID)”和组件的“ObjectSearchUpdated”函数。

有没有一种我完全想念的相当直接的方法来实现这一点?我是不是走在了正确的轨道上,并且只需要对现有代码进行大量重构才能使事情正常运行?

【问题讨论】:

我能想象的是让IDDropdownDisplayInfo 属性的名称像&lt;SearchableDropdown DropdownItems="AvailableClients" IdProperty="ID"&gt;&lt;/SearchableDropdown&gt; 一样可配置。我不确定这是否使它更可重用。 这仍然存在函数获取适当数据的问题。我也可以将这些 EventCallbacks 传递给组件,但我再次觉得这部分地使首先拥有组件的观点无效。 【参考方案1】:

如果无法实现接口方式,或许您可以尝试创建一个通用代理对象来传递参数。

例如,您可以尝试使用 LINQ 将 IdNameDelegate 作为代理列表传递。

在 OnInitialized 中或在您启动它的任何地方准备您的参数。

这是一个简单的概念,展示了如何使用委托。

委托数据代理类初始化:

    public delegate void OnClick(UIEventArgs eventArgs, int id, string name);

    private class ExampleData
    
        // let's assume that it has different naming convention for your case
        public int IdValue get;set;
        public string Fullname get;set;
    

    private class SearchableDropdownItem 
    
        public int Id get;set;
        public string Name get;set;
        public OnClick OnClicked get;set;
    

代理创建示例:

    public void Prepare()
    
        var dataList = new List<ExampleData>();
        for(int i=0; i<3; i++)
        
            dataList.Add(new ExampleData()
                IdValue = i+1,
                Fullname = "test" + (i + 1)
            );
        

        var searchableItemList = dataList.Select(x => new SearchableDropdownItem()
            // here is how you can set callback and fields
            Id = x.IdValue,
            Name = x.Fullname,
            OnClicked = new OnClick(SetClientID)
        ).ToList();
    

    public void SetClientID(UIEventArgs eventArgs, int id, string name)
    
        // implement your code
    

并将 searchableItemList 传递给组件并使用它:

<SearchableDropdown ItemList="searchableItemList"></SearchableDropdown>

...

@foreach (var item in searchableItemList)

    <li id="@item.Id" @onclick="@(e => item.OnClicked(e,client.Id,client.Name))">@item.Name</li>

【讨论】:

以上是关于如何制作一个可以接收不同对象列表作为参数的通用 Blazor 组件?的主要内容,如果未能解决你的问题,请参考以下文章

Java 中类似 Python 的列表推导

如何在我的自定义 JSP 标记中将整数值作为参数传递?

如何找到两个不同音频样本之间的比例?

打字稿:如何制作接受对象的类型,其键匹配通用但所有值都是值参数的映射函数

如何将类对象作为参数传递给不同的类方法?

apply 和 call 的区别