【问题标题】:Communicating from child to parent when using Components in Blazor在 Blazor 中使用组件时从子级到父级进行通信
【发布时间】:2021-04-20 11:03:55
【问题描述】:

我有一个博客网站,其中 cmets 在名为 CommentListComponent 中进行管理。

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);
}

正如您在上面的代码中看到的,在我的组件中,我通过服务检索了这篇文章的所有 cmets,然后我调用了一个方法来通知父级计数。

我需要在检索到 cmets 列表后立即将计数传达给父级。我找到的唯一重新计算此计数 (CountingComments) 的地方是 OnParametersSetAsync

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

  • OnParametersSetAsync 呼叫CountingComments
  • CountingComments 调用OnCommentCountChanged
  • 此“调用”会产生对OnParametersSetAsync 的调用
  • 等等……

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

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

【问题讨论】:

  • 为什么 OnInitializedAsync 不起作用?它被调用一次。
  • 因为我的服务应该首先以 Id 作为参数进行获取,并且在 OnParameterSetAsync 中检索此参数。我不认为 OnInitializedAsync 中已经设置了 Id 参数。

标签: blazor blazor-webassembly blazor-client-side


【解决方案1】:

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

现在采用这种设计,只有在当前帖子(博客的,请注意)中添加新评论时,您才需要通过再次调用 CommentService.GetComments 方法来刷新 AllComments 列表。如何调用、何时调用等等,都取决于你的设计。

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

很遗憾,无法区分每个参数的变化

为什么不呢?

这就是你应该如何定义你的[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;
       }
    }

【讨论】:

  • 感谢您的清晰解释,它现在按预期工作。但是我不明白你的Note所谓的即发即弃。
  • 糟糕...我尝试将一些代码放入 InternalPostId 的设置器中(在 if 条件的内部和外部)。代码永远无法到达。即使我从索引中更改 PostId。仅调用 OnParametersSet。有什么想法吗?
  • 为什么你的服务中没有一个事件——比如CommentListChanged,只要你调用CommentService.GetComments(PostId),它就会被调用。 Index.razor 注入服务,向CommentListChanged 注册事件处理程序,将计数显示链接到服务上的公共属性CommentsCount,然后调用StateHasChanged。这摆脱了你所有的反向接线和箍跳。如果您愿意,我可以发布或指出一些示例代码。
  • @MrCakaShaunCurtis 我同意它可以像你说的那样完成,但作为一个练习,我想将调用保留在子组件中。一个组件中经常有多个参数。所以我现在的问题是(如我之前的评论中所解释的)我将获取代码移动到 OnInitialisedAsync 中,并尝试按照 enet 的解释实现 InternalPostId,但不幸的是它不起作用。我的意思是永远无法到达 setter 中的代码。
  • @Bronzato,请参阅我的回答中的更新部分。至于第一条评论,我会尽快回答你的问题。
猜你喜欢
  • 2016-09-23
  • 2023-03-31
  • 2017-08-16
  • 1970-01-01
  • 2021-09-19
  • 2020-01-18
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多