7

Why the Blazor UI doesn't update after delete event:

My Component:

<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>

My Component code behind:

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

        }
    }
}

The Delete Method:

public async Task DeleteTrainingTechnique(int id)
{
    await _httpClient.DeleteAsync($"training/trainingtechniques/{id}");
}
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
Anyname Donotcare
  • 11,113
  • 66
  • 219
  • 392

5 Answers5

10

Solution 1: Reload the TrainingTechniques list from the source

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

Solution 2: Remove the item form the list:

<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>

And the Delete method:

private async Task Delete(int id)
{
    TrainingTechniques.RemoveAll(x => x.Id == Id);
    await TrainingTechniqueConsumer.DeleteTrainingTechnique(id);
}
tbdrz
  • 1,811
  • 2
  • 12
  • 30
  • Thanks a lot; Which solution U prefer, I think the second one is more efficient because no second trip to the database, but Could I ask If this will get the correct index if I make paging(multiple page) and sorting? – Anyname Donotcare Apr 10 '20 at 19:34
  • The second one will indeed give a better user experience. I have edited the anwens with delete by Id instead of by index, so there is abslote no problem with sorting and stuff – tbdrz Apr 10 '20 at 20:07
2

Remove the item from the list that has the binding to the UI.

protected async Task DeleteTechnique(int id)
{
    await (TrainingTechniqueConsumer.DeleteTrainingTechnique(id));
    TrainingTechniques.Remove(TrainingTechniques.Single(x => x.Id == id));
}

I think it is better to pass the item it self instead of an Id. For example the delete could be:

protected async Task DeleteTechnique(TrainingTechniqueView corpse)
{
    await (TrainingTechniqueConsumer.DeleteTrainingTechnique(corpse.Id));
    TrainingTechniques.Remove(corpse);
}
Zsolt Bendes
  • 2,219
  • 12
  • 18
2

I had the same problem and almost same UI as yours but my problem was instead of defining TrainingTechniques as List, I'd defined it as IEnumerable. So instead of:

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

I changed it as:

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

And then using one of solutions suggested by @Inktkiller.

VahidShir
  • 2,066
  • 2
  • 17
  • 27
1

Below code is working for me-

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

        }
    }
}
Ashiquzzaman
  • 5,129
  • 3
  • 27
  • 38
1

You are correctly deleting the item on the backend.
But in order to see the effect you will have to reload the data:

protected async Task DeleteTechnique(int id)
{
   await (TrainingTechniqueConsumer.DeleteTrainingTechnique(id));
   TrainingTechniques = (await TrainingTechniqueConsumer.GetTechniques()).ToList();
 //this.StateHasChanged(); -- not necessary
}

As an alternatice you could delete the item form the already loaded TrainingTechniques, a small optimization but you would then run the risk of concurrency errors.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    Ah I just saw that Ink Killer already answered this. Still, this is the core. – H H Apr 12 '20 at 00:55
  • Thanks a lot, Should i use lock to avoid the risk of concurrent delete from the list،because I thought if i have a big list it will be a big optimization to delete from it rather than getting the whole data again. Or You recommend another solution? – Anyname Donotcare Apr 12 '20 at 02:09
  • "Should i use lock.." - no, all your code runs on the same thread, don't fix what isn't broken. – H H Apr 12 '20 at 08:06
  • "will be a big optimization" - yes, but at the cost of of being current. How many users will edit this data? – H H Apr 12 '20 at 08:08
  • about 5-7 users – Anyname Donotcare Apr 12 '20 at 09:21