Blazor:如何在通用组件中将事件传递给onClick函数?

5
我正在为一个网站制作表格组件。我希望能够在同一页上创建、更新、添加和删除表格条目。我还将在整个网站上使用此组件,并且需要许多种类的类作为输入。我去这里学习如何实现。我还想在内部组件中设置每行的可编辑类。以下是我的代码:

Editable Class.Razor

@using WbotV2.Data
@using System
@using System.Collections
@using System.Reflection
@typeparam TItem

    <tr>
        @foreach (var datum in Data)
        {
            <td>@datum</td>
        }
        <td><button class="btn btn-success">Edit</button></td>
        <td>@ChildContent</td>
    </tr>


@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

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

    public List<string> Data;
    public bool Editable = false;
    protected override async Task OnInitializedAsync()
    {
        Data = GetData(Item);
    }

    public static List<string> GetData<T>(T instance)
    {
        List<string> ret = new List<string>();
        FieldInfo[] infos = typeof(T).GetFields();
        foreach (FieldInfo info in infos)
        {
            ret.Add(info.GetValue(instance).ToString());
        }
        return ret;
    }
}

这个组件应该允许我将一个类作为参数传递,并编辑它的字段。如果可能的话,它也不应该将用户带到单独的页面来进行编辑(我希望它可以内联在表格中)。我的想法是使用绑定来实现这一点,但是由于类名是任意的,所以我不知道如何将其传递给@bind,这就是我卡住的地方。 表格组件.Table Component.razor
@using WbotV2.Data
@using System
@using System.Collections
@using System.Reflection
@typeparam TItem

<table class="table">
    <thead>
        <tr>
            @foreach (string header in Headers)
            {
                <th>@header</th>   
            }
            <td>Edit</td>
            <td>Delete</td>
            <td><button class="btn btn-primary">Add</button></td>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <EditableClass Item="@item"><button class="btn btn-danger"  @onclick="(Delete(item))">Delete</button></EditableClass>
        }
    </tbody>
</table>


@code {
        [Parameter] 
        public IList<TItem> Items { get; set; }

        [Parameter] 
        public TableUtilityClass<TItem> DataInstance { get; set; }

    public string[] Headers;

    protected override async Task OnInitializedAsync()
    {
        Headers = GetHeaders();
    }

    public string[] GetHeaders()
    {
        List<string> ret = new List<string>();
        FieldInfo[] infos = typeof(TItem).GetFields();
        foreach (FieldInfo info in infos)
        {
            ret.Add(info.Name);
        }
        return ret.ToArray();
    }

    public void Delete(TItem itemToDelete)
    {
        DataInstance.Delete(itemToDelete);
        StateHasChanged();
        Console.WriteLine("Here");
    }

    public void Add(TItem itemToAdd)
    {
        DataInstance.Add(itemToAdd);
        StateHasChanged();
        Console.WriteLine("Here");
    }

    public List<List<string>> GetDataString<T>(IEnumerable<T> instance)
    {
        List<List<string>> ret = new List<List<string>>();
        foreach (T item in instance)
        {
            ret.Add(new List<string>());
            FieldInfo[] infos = typeof(T).GetFields();
            foreach (FieldInfo info in infos)
            {
                ret[ret.Count - 1].Add(info.GetValue(item).ToString());
                Console.WriteLine($"{info.Name}: {info.GetValue(item)}");
            }
        }
        return ret;
    }
}

这个组件应该在表格中包含许多可编辑的类组件,并处理向其中添加和删除新条目。我目前遇到的问题是删除,我尝试实现this solution,但是在这一行出现了“无法将void转换为Microsoft.AspNetCore.Components.EventCallback”错误:<EditableClass Item="@item"><button class="btn btn-danger" @onclick="(Delete(item))">Delete</button></EditableClass> 我也尝试过thisthis。我希望能在这里得到一些帮助,无论是解决错误还是找到一个巧妙的方法来解决它。

Table Utility Class.cs

using System.Threading.Tasks;

namespace WbotV2.Data
{
    public abstract class TableUtilityClass<T>
    {
        public abstract Task Delete(T Item);
        public abstract Task Add(T Item);
    }
}

这个类应该有助于与许多不同类型的服务进行交互,因为我预计会有很多这样的服务。
最后,这是我的Countries.razor(放置表格组件的页面)。
@page "/countries"

@using WbotV2.Data
@inject WbotCountryService CountryService


<h1>Country Information</h1>


@if (countries == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <TableComponent Items="@countries" DataInstance="@CountryService"/>
}

@code {

    public List<WbotCountry> countries;

    protected override async Task OnInitializedAsync()
    {
        countries = await CountryService.GetCountriesAsync();
    }
}

对于那些想知道Country服务是什么的人,它看起来像这样:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WbotV2.Data
{
    public class WbotCountryService : TableUtilityClass<WbotCountry>
    {
        public WbotData WbotDataInstance;

        public WbotCountryService()
        {
            WbotDataInstance = WbotData.LoadData();
        }
        public Task<List<WbotCountry>> GetCountriesAsync()
        {
            return Task.FromResult(WbotDataInstance.countries);
        }

        public override Task Delete(WbotCountry c)
        {
            WbotDataInstance.countries.Add(c);
            WbotDataInstance.SaveData();

            return Task.CompletedTask;
        }
        public override Task Add(WbotCountry c)
        {
            WbotDataInstance.countries.Remove(c);
            WbotDataInstance.SaveData();

            return Task.CompletedTask;
        }
    }
}

任何帮助都将不胜感激!

1
显然 @onclick="(Delete(item))"> 是错误的,为什么 @onclick="@( ()=>(Delete(item)))"> 对你来说不是一个解决方案呢?应该可以工作。 - dani herrera
1
只是想说我尊重 foreach (var datum in data) 这个语句。 - Eliasar
1个回答

4

这是有效的:

EditableClass.razor

@using WbotV2.Data
@using System
@using System.Collections
@using System.Reflection
@typeparam TItem

<tr>
    @foreach (var datum in Data)
    {
        <td>@datum</td>
    }
    <td><button class="btn btn-success">Edit</button></td>
    <td>@ChildContent</td>
</tr>


@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
    [Parameter]
    public EventCallback<TItem> Delete { get; set; }

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

    public List<string> Data;
    public bool Editable = false;
    protected override void OnInitialized()
    {
        Data = GetData(Item);
    }

    public static List<string> GetData(TItem instance)
    {
        List<string> ret = new List<string>();
        FieldInfo[] infos = typeof(TItem).GetFields(BindingFlags.NonPublic | BindingFlags.Instance
            | BindingFlags.Public);
        foreach (FieldInfo info in infos)
        {
            ret.Add(info.GetValue(instance).ToString());
        }
        return ret;
    }
}

请注意我所做的更改以及我添加的代码。

TableComponent.razor

<EditableClass Item="@item"><button class="btn btn-danger" @onclick="@(() => Delete(item))">DeleteMe</button></EditableClass>


public void Delete(TItem itemToDelete)
    {
        DataInstance.Delete(itemToDelete);
      //  StateHasChanged();
       // Console.WriteLine("Here");
    }

还有其他问题,但现在我没有时间。我会尽力进一步改善它。这项服务可以向您展示如何创建您的服务以及它如何与组件通信:https://github.com/aspnet/samples/blob/master/samples/aspnetcore/blazor/FlightFinder/FlightFinder.Client/Services/AppState.cs

希望这能有所帮助...


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