blazorblazor-webassemblyblazor-routing

Blazor WASM Update Navigation when Collection Changes


My planned design is to have a home page with 4 buttons. When a user clicks any of these buttons it adds an item to the navigation. I used this singleton to manage state

public class StateManager
{
    public event Action NewPlanAdded;
    public void AddPlan()
    {
        NewPlanAdded?.Invoke();
    }
}

I injected this singleton into the child component

@inject StateManager StateManager

<button class="btn btn-primary" @onclick="CreateNewPlan">Create New Plan</button>

@code {
    private void CreateNewPlan()
    {
        StateManager.AddPlan();
    }
}

And the navigation component

@inject StateManager StateManager

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        @foreach (var item in Items)
        {
            <li class="nav-item px-3">
                <NavLink class="nav-link" href="@item.Item1" >
                    <span class="@item.Item2" aria-hidden="true"></span> @item.Item3
                </NavLink>
            </li>
        }        
    </ul>
</div>

@code {
    IList<Tuple<string, string, string>> Items { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        StateManager.NewPlanAdded += NewPlanAdded;
        Items = new List<Tuple<string, string, string>>
        {
            new Tuple<string, string, string>("", "oi oi-home", "Home")
        };
    }

    public void Dispose()
    {
        StateManager.NewPlanAdded -= NewPlanAdded;
    }

    private void NewPlanAdded()
    {
        Items.Add(new Tuple<string, string, string>("plan", "oi oi-plus", "Plan (unsaved)"));
    }
}

The problem is that the new list items don't appear until the navigation component is hidden and then displayed again. How do I get the foreach to refresh when the collection changes?


Solution

  • You should to inform your component that state has been changed executing InvokeAsync(StateHasChanged):

    private void NewPlanAdded()
    {
        Items.Add(new Tuple<string, string, string>("plan", "oi oi-plus", "Plan (unsaved)"));
        InvokeAsync(StateHasChanged);  //<--- this line
    }
    

    Realize that, in your scenario, just to invoke StateHasChanged() it's not enough, you should to do it from right thread (using InvokeAsync)