在 Blazor 中使用组件时从 child 通信到 parent

Communicating from child to parent when using Components in Blazor

我有一个博客网站,在一个名为 CommentList.

Component 中管理评论
index.razor

...
Counting comments: @CommentCount
<CommentList PostId="@CurrentPost.Id" OnCommentCountChanged="OnCommentCountChangedHandler" />

@code {
    ...
    int CommentCount;
    public void OnCommentCountChangedHandler(int count)
    {
        CommentCount = count;
    }
}

现在组件:

CommentList.razor

[Parameter] public int PostId { get; set; }
[Parameter] public EventCallback<int> OnCommentCountChanged { get; set; }
[Inject] ICommentService CommentService { get; set; }

List<Comment> AllComments { get; set; }    
bool flag;

protected override async Task OnParametersSetAsync()
{
    if (PostId && !flag)
    {
        flag = true;
        AllComments = await CommentService.GetComments(PostId);
        await CountComments();
    }
}
protected async Task CountComments()
{
    Console.WriteLine("CountComments called");
    if (AllComments == null) return;
    var count = AllComments.Count();
    await OnCommentCountChanged.InvokeAsync(count);
}

正如您在上面的代码中看到的,在我的组件中,我通过服务检索了此 post 的所有评论,然后我调用了一个方法来通知 parent 计数。

我需要在检索到评论列表后立即将计数传达给 parent。我找到的用于重新计算此计数 (CountingComments) 的唯一位置是 OnParametersSetAsync.

你会注意到我使用了一面旗帜。没有这个标志,就会有一个永无止境的循环:

有了标志,就避免了循环,但我想知道这是否是最好的方法?

遗憾的是,无法区分每个参数更改。如果我们有 2 个或 3 个参数,如果其中一个参数发生更改,则会触发 OnParametersSetAsync 方法,但我们不知道与哪个参数有关。此外,调用 EventCallback Parameter OnCommentCountChanged 也会毫无兴趣地触发此 OnParametersSetAsync(在我的情况下)。

OnParametersSetAsync 方法中的代码在每次执行该方法时都会多余地检索注释。相反,您应该在创建 CommentList 组件时使用 OnInitalized(Async) 对方法仅检索一次数据。

现在采用该设计,只有当新评论添加到当前 post(请注意博客的)时,您才需要通过调用CommentService.GetComments 方法再次。怎么调用,什么时候调用等等,全看你的设计了。

让我在这里描述一个简单可行的设计。在您博客的底部 post 是一个评论部分,您可以在其中输入评论,然后单击“保存我的评论”按钮。您可以定义服务或 Razor 组件,以 post 将添加的评论添加到相关数据库 table(s)。该服务或组件还应该有代码来调用一个事件,通知您的 CommentList 发生了这种情况(添加了新评论)。作为响应,您的代码应该调用 CommentService.GetComments 方法从数据库中检索一组新的评论。

Unfortunately it is not possible to make the distinction between each parameter changes

为什么不呢?

这就是您应该定义 [Parameter] public int PostId { get; set; }

的方式
[Parameter] public int PostId { get; set; }

private int _internalPostId;
private int InternalPostId 
{ 
   get => _internalPostId; 
   set 
   {
     if(_internalPostId != value)
     {
        _internalPostId = value;

        // Can only be true if a new post is displayed or selected 
        // in the Index component, which requires you to retrieve the 
        // comments for the new post id passed. Put here code that 
        // call the method that perform the database access to
        // retrieve the comments. That should be the same code that 
        // you should have in OnInitalized(Async) pair of methods. So 
        // you can create a single method that should be called from 
        // OnInitalized(Async), and from here.
        // Understand that OnInitalized(Async) is called only once, 
        // when the component is created... When the PostId parameter 
        // changes, the Component is not created again, but re-used.
        // Note: If you define an asynchronous method that contain the 
        // code to retrieve the comments, you can still call it from 
        // here by applying the so-called fire-and-forget operation, 
        // as for instance:  _ = MyService.GetSomething();
     }
   } 
}

请注意,我引入了一个名为 InternalPostId 的新私有 属性。使用它是因为参数属性不应该被子组件改变......它们应该被视为自动属性。希望大家不要迷惑。

您还需要覆盖 OnParametersSet 方法才能更新 来自 PostId 参数 属性.

的内部 属性 InternalPostId
 protected override void OnParametersSet()
    {
       if (InternalPostId != PostId)
       {
           InternalPostId = PostId;
       }
    }