为什么 Blazor UI 组件在删除事件后不会更新?

8
为什么在删除事件后Blazor UI不更新:
我的组件:
<table class="table table-striped">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Example</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var trainingTechnique in TrainingTechniques) {
        <tr>
            <td>@trainingTechnique.Id</td>
            <td>@trainingTechnique.Name</td>
            <td>@trainingTechnique.Example</td>
            <td>
                <button type="button" 
                        class="btn btn-danger" 
                        @onclick="@(async () => await DeleteTechnique(trainingTechnique.Id))">
                    Delete
                </button>

            </td>
        </tr>
        }
    </tbody>
</table>

我的组件代码后端:

public class TrainingTechniqueViewPageBase : ComponentBase
    {
        public List<TrainingTechniqueView> TrainingTechniques { get; set; }
        [Inject] 
        public ITrainingTechniqueConsumer TrainingTechniqueConsumer { get; set; }

        protected TrainingTechniqueForm TrainingTechniqueForm { get; set; } 
        protected override async Task OnInitializedAsync()
        {
            TrainingTechniques = (await TrainingTechniqueConsumer.GetTechniques()).ToList();
        }

        public async void TrainingTechniqueForm_OnSave()
        {
            TrainingTechniques = (await TrainingTechniqueConsumer.GetTechniques()).ToList();
            StateHasChanged();
        }

        protected void AddTrainingTechnique()
        {
            TrainingTechniqueForm.Show();
        }

        protected async Task DeleteTechnique(int id)
        {
           await (TrainingTechniqueConsumer.DeleteTrainingTechnique(id));
            this.StateHasChanged();

        }
    }
}

删除方法:

public async Task DeleteTrainingTechnique(int id)
{
    await _httpClient.DeleteAsync($"training/trainingtechniques/{id}");
}

您的问题在当时被关闭,因为它与当时的问题有很大的差异。如果您想要重新开放它,请提供一个最小化且完整可复现的示例(MCVE)。 - mjwills
你显然编辑了你的问题以使其工作? - Vencovsky
@Vencovsky 不是的,删除后界面DOM仍然没有重新绘制,我必须导航离开该页面并返回才能更新。我无法识别问题所在。 - Anyname Donotcare
1
@任意名称无所谓 你能分享一下这个列表项的get方法吗? - Ashiquzzaman
@Ashiquzzaman 我已经更新了问题并添加了更多的代码。 - Anyname Donotcare
5个回答

10

解决方案1: 从源重新加载TrainingTechniques列表

private async Task Delete(int id)
{
    await TrainingTechniqueConsumer.DeleteTrainingTechnique(id);
    TrainingTechniques = (await TrainingTechniqueConsumer.GetTechniques()).ToList();
}

解决方案2:从列表中移除该项:

<table class="table table-striped">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Example</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var trainingTechnique in TrainingTechniques)
        {
            <tr>
                <td>@trainingTechnique.Id</td>
                <td>@trainingTechnique.Name</td>
                <td>@trainingTechnique.Example</td>
                <td>
                    <button type="button" class="btn btn-danger" @onclick="@(() => DeleteTechnique(trainingTechnique.Id))">Delete</button>
                </td>
            </tr>
        }
    </tbody>
</table>

还有 Delete 方法:

private async Task Delete(int id)
{
    TrainingTechniques.RemoveAll(x => x.Id == Id);
    await TrainingTechniqueConsumer.DeleteTrainingTechnique(id);
}

非常感谢;您更喜欢哪个解决方案,我认为第二个更有效率,因为不需要第二次访问数据库,但是我可以问一下,如果我进行分页(多页)和排序,这是否会得到正确的索引? - Anyname Donotcare
第二个确实会提供更好的用户体验。我已经使用按ID而不是按索引编辑了anwens,因此在排序和其他方面绝对没有任何问题。 - tbdrz

2

我曾经遇到过和你类似的问题,界面也几乎一样,但是我的问题是将TrainingTechniques定义为IEnumerable而不是List。所以应该这样写:

public IEnumerable<TrainingTechniqueView> TrainingTechniques { get; set; }

我把它改成了:
public List<TrainingTechniqueView> TrainingTechniques { get; set; }

然后使用@Inktkiller建议的其中一种解决方案。

2
从绑定到UI的列表中删除该项。
protected async Task DeleteTechnique(int id)
{
    await (TrainingTechniqueConsumer.DeleteTrainingTechnique(id));
    TrainingTechniques.Remove(TrainingTechniques.Single(x => x.Id == id));
}

我认为传递项目本身而不是ID更好。例如,删除操作可以如下:

protected async Task DeleteTechnique(TrainingTechniqueView corpse)
{
    await (TrainingTechniqueConsumer.DeleteTrainingTechnique(corpse.Id));
    TrainingTechniques.Remove(corpse);
}

1

以下代码可以正常工作:

TrainingTechnique.razor:

<table class="table table-striped">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Example</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var trainingTechnique in TrainingTechniques)
        {
            <tr>
                <td>@trainingTechnique.Id</td>
                <td>@trainingTechnique.Name</td>
                <td>@trainingTechnique.Example</td>
                <td>
                    <button type="button" class="btn btn-danger" @onclick="@(async () => await DeleteTechnique(trainingTechnique.Id))">Delete</button>

                </td>
            </tr>
        }
    </tbody>
</table>

Component:

    public class TrainingTechniqueViewPageBase : ComponentBase
    {
        public List<TrainingTechniqueView> TrainingTechniques { get; set; }
        [Inject] 
        public ITrainingTechniqueConsumer TrainingTechniqueConsumer { get; set; }

        protected TrainingTechniqueForm TrainingTechniqueForm { get; set; } 
        protected override async Task OnInitializedAsync()
        {
            TrainingTechniques = await TrainingTechniqueConsumer.GetTechniques().ToListAsync();
        }

        public async void TrainingTechniqueForm_OnSave()
        {
            TrainingTechniques = await TrainingTechniqueConsumer.GetTechniques().ToListAsync();
           await InvokeAsync(() =>
            {
                StateHasChanged();
            });
        }

        protected void AddTrainingTechnique()
        {
            TrainingTechniqueForm.Show();
        }

        protected async Task DeleteTechnique(int id)
        {
           await TrainingTechniqueConsumer.DeleteTrainingTechnique(id);
           TrainingTechniques =null;
           TrainingTechniques = await TrainingTechniqueConsumer.GetTechniques().ToListAsync();
           await InvokeAsync(() =>
            {
                StateHasChanged();
            });

        }
    }
}

1
你正确地在后端删除了该项。
但为了看到效果,你需要重新加载数据:
protected async Task DeleteTechnique(int id)
{
   await (TrainingTechniqueConsumer.DeleteTrainingTechnique(id));
   TrainingTechniques = (await TrainingTechniqueConsumer.GetTechniques()).ToList();
 //this.StateHasChanged(); -- not necessary
}

作为一种替代方法,您可以从已加载的TrainingTechniques中删除该项,这是一个小优化,但您将面临并发错误的风险。

1
啊,我刚看到Ink Killer已经回答了这个问题。不过,这是核心。 - H H
非常感谢,我应该使用锁来避免从列表中并发删除的风险吗?因为我认为如果我有一个大列表,从中删除会比重新获取整个数据更好。或者您推荐另一种解决方案吗? - Anyname Donotcare
“我应该使用锁..” - 不需要,你的所有代码都在同一个线程上运行,不要修复不需要修复的问题。 - H H
"将是一个很大的优化" - 是的,但代价是当前的。有多少用户会编辑这些数据? - H H
大约5-7个用户 - Anyname Donotcare

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