【问题标题】:Passing an event from a dynamically rendered component up to parent and / or Grandparent将事件从动态呈现的组件传递到父级和/或祖父级
【发布时间】:2022-10-23 18:36:40
【问题描述】:

我正在尝试为 Blazor 客户端 SPA 创建表组件,该组件根据传递的项目实现行和列的动态显示。 到目前为止,我已经设法完成了行和列的显示。现在我希望实现排序,我将通过在标题列中设置一个排序按钮来实现。

到目前为止,我有 3 个组件:

DataTable 组件(父组件)

  • DataTable 列组件(子组件)
    • DataTable 排序组件(数据表组件的子级)

以下是代码的精简版本:

页面.razor

@page "/transactions/list"
@using Accounting.Web.Components.Grid;
@using Accounting.Web.Components.DataTable;

<h3>List</h3>

<DataTable Items="users">
    <DataTableColumn TRowData="User" Expression="u => u.Id"/>
    <DataTableColumn TRowData="User" Expression="u => u.Username" />
    <DataTableColumn TRowData="User" Expression="u => u.DateOfBirth" />
    <DataTableColumn TRowData="User"><div>Column B</div></DataTableColumn>
</DataTable>


@code {

    public class User
    {
        public int Id { get; set; }
        public string Username { get; set; }
        public DateTime DateOfBirth { get; set; }
    }

    public List<User> users { get; set; } = new (){
        new User{
            Id = 1,
            Username = "Me",
            DateOfBirth = new DateTime(1981, 12, 23)
        },
        new User{
            Id = 2,
            Username = "You",
            DateOfBirth = new DateTime(1980, 1, 1)
        }
    };
}   

数据表 Razor.razor

数据表呈现数据源中的每个列标题和行列

@typeparam TRowData <CascadingValue IsFixed="true" Value="this">@ChildContent</CascadingValue> (Items)

    @* Render the table *@
    <table>
        <thead>
            <tr>
                @foreach (var column in columns)
                {
                    @column.HeaderTemplate;
                }
            </tr>
        </thead>
        <tbody>
            @{
                if (Items != null)
                {
                    var index = 0;
                    foreach (var item in Items)
                    {
                            @* Use @key to help the diff algorithm when updating the collection *@
                            <tr>
                                @foreach (var column in columns)
                            {
                                    @column.CellTemplate(item);
                            }
                            </tr>
                    }
                }
            }
        </tbody>
    </table>
    
    @code {
    
        [Parameter]
        public ICollection<TRowData> Items { get; set; }
    
        [Parameter]
        public RenderFragment ChildContent { get; set; }
    
        private readonly List<DataTableColumn<TRowData>> columns = new();
    
        internal void AddColumn(DataTableColumn<TRowData> column)
        {
            columns.Add(column);
        }
    
        protected override void OnAfterRender(bool firstRender)
        {
            if (firstRender)
            {
                StateHasChanged();
            } 
        }
    
    }

数据表列.razor

@typeparam TRowData

@using System.Linq.Expressions

@code {
    [CascadingParameter]
    public DataTable<TRowData> Owner { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public bool Sortable { get; set; }

    [Parameter]
    public string Format { get; set; }

    [Parameter]
    public Expression<Func<TRowData, object>> Expression { get; set; }

    [Parameter]
    public RenderFragment<TRowData> ChildContent { get; set; }

    private RenderFragment<TRowData> cellTemplate;
    private RenderFragment headerTemplate;
    private Func<TRowData, object> compiledExpression;
    private Expression lastCompiledExpression;

    public void test()
    {
        System.Console.WriteLine("test");
    }

    internal RenderFragment HeaderTemplate
    {
        get
        {
            return headerTemplate = (builder =>
            {
                var title = Title;

                if (title == null && Expression != null)
                {
                    title = GetMemberName(Expression);
                }

                builder.OpenElement(0, "th");
                builder.AddContent(1, title);

                if (Sortable)
                {
                    builder.OpenComponent(0, typeof(DataTableSort));
                    builder.CloseComponent();                
                }

                builder.CloseElement();
            });
        }
    }

    internal RenderFragment<TRowData> CellTemplate
    {
        get
        {
            return cellTemplate ??= (rowData => builder =>
            {
                builder.OpenElement(0, "td");

                if (compiledExpression != null)
                {
                    var value = compiledExpression(rowData);
                    var formattedValue = string.IsNullOrEmpty(Format) ? value?.ToString() : string.Format("{0:" + Format + "}", value);
                    builder.AddContent(1, formattedValue);
                } 
                else
                {
                    builder.AddContent(1, ChildContent, rowData);                    
                }                
                builder.CloseElement();
            });
        }
    }


    protected override void OnInitialized()
    {
        Owner.AddColumn(this);
    }

    protected override void OnParametersSet()
    {
        if (lastCompiledExpression != Expression)
        {
            compiledExpression = Expression?.Compile();
            lastCompiledExpression = Expression;
        }
    }

    private static string GetMemberName<T>(Expression<T> expression)
    {
        return expression.Body switch
        {
            MemberExpression m => m.Member.Name,
            UnaryExpression u when u.Operand is MemberExpression m => m.Member.Name,
            _ => throw new NotSupportedException("Expression of type '" + expression.GetType().ToString() + "' is not supported")
        };
    }
}

上面的代码取自我正在使用的在线教程。

现在在 HeaderTemplate RenderFragment 方法中,您可以看到我正在渲染另一个组件:DataTableSort,请参见以下内容:

<button @onclick="onClicked">sort</button>

@code {
    protected async Task onClicked()
    {
        System.Console.WriteLine("sort");
    }
}

这就是我迷路的地方。我需要它,以便在按下按钮时,将一个事件传递给父 Datatable 列组件或 DataTable 祖父组件,我可以在其中根据单击的排序按钮对项目进行排序。

解决此问题的最佳方法是什么?

【问题讨论】:

    标签: blazor blazor-webassembly


    【解决方案1】:

    我使用ListContext 类。顶级组件创建一个实例并级联它。任何子组件都可以在上下文中引发事件,任何需要对事件做出反应的组件都可以注册事件处理程序。我的看起来像这样:

    public sealed class ListContext
    {
        public event EventHandler<SortRequest>? SortingRequested;
        public event EventHandler<PagingRequest?>? PagingRequested;
        public event EventHandler<EventArgs>? ListChanged;
        public event EventHandler<EventArgs>? PagingReset;
    
        public ListContext() { }
    
        public void NotifyPagingRequested(object? sender, PagingRequest? request)
            => this.PagingRequested?.Invoke(sender, request);
    
        public void NotifySortingRequested(object? sender, SortRequest request)
            => this.SortingRequested?.Invoke(sender, request);
    
        public void NotifyPagingReset(object? sender)
            => this.PagingReset?.Invoke(sender, EventArgs.Empty);
    
        public void NotifyListChanged(object? sender)
            => this.ListChanged?.Invoke(sender, EventArgs.Empty);
    }
    

    作为参考,请求对象:

    public record SortRequest
    {
        public string? SortField { get; init; }
        public bool SortDescending { get; init; }
        public bool IsSorting => !string.IsNullOrWhiteSpace(SortField);
    }
    
    public record PagingRequest
    {
        public int PageSize { get; init; } = 1000;
        public int StartIndex { get; init; } = 0;
        public int Page => StartIndex <= 0
            ? 0
            : StartIndex / PageSize;
    
        public PagingRequest() { }
        public PagingRequest(int page)
            => this.StartIndex = PageSize * 0;
    }
    

    【讨论】:

    • 您的回答表明您已经创建了一个类似于我的组件来处理排序和分页。你有完整组件代码的链接吗?查看并从中了解更多信息会很有趣
    • 是的,但目前它是私有的。您需要采用其他一些编码实践/策略才能使用它。我目前正在整理一个 GitHub 存储库以将其公开,但要准备好一个月左右。
    • 如果您可以在回购可用时在此处发布链接,那就太好了。这将帮助我和许多像我一样学习 Blazor 的人
    • 我已经为您的问题添加了书签并添加了提醒,所以是的,我会这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-24
    • 2017-11-07
    • 2020-10-08
    • 2018-07-31
    • 1970-01-01
    相关资源
    最近更新 更多