I've got a blazor project and created some tab components. I want these tabs to be dynamic so I can add and take away from them when new values get added to the view model.
Adding works fine but once the page has rendered and I try to remove one of the tabs it doesn't update the tab header and I can't work out why as the view model has changed so surely the page should re-render?
The count updates correctly but the page is not re-rendering to remove the tab. I'm sure I'm fundamentally missing something.
I've created a fiddle to show this better than I can explain: https://blazorfiddle.com/s/fduse7v2
Thanks in advance.
EDIT - Added code from fiddle as requested.
//INDEX
@page "/"
<h1>Tab Issue</h1>
Data Count: @View.Data.Count
<button @onclick='(() => Add($"Tab {(View.Data.Count + 1)}"))'>Add Tab</button>
<TabControl>
@foreach (var data in View.Data)
{
<TabPage Text='@data'>
@data <button @onclick="(() => Remove(data))">Remove</button>
</TabPage>
}
</TabControl>
@code{
public class ViewModel{
public List<string> Data = new List<string>{
"Tab 1","Tab 2","Tab 3"
};
}
public ViewModel View { get; set; }
protected override async Task OnInitializedAsync()
{
View = new ViewModel();
}
public void Add(string data)
{
View.Data.Add(data);
StateHasChanged();
}
public void Remove(string data)
{
View.Data.Remove(data);
StateHasChanged();
}
}
//TabPage
@if (Parent.ActivePage == this)
{
<div class='tab-page @($"tab-{this.Text.Replace(" ","-").ToLower()}")'>
@ChildContent
</div>
}
@code {
[CascadingParameter] public TabControl Parent { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public string Id { get; set; }
[Parameter] public string Text { get; set; }
protected override void OnInitialized()
{
if (Parent == null)
throw new ArgumentNullException(nameof(Parent), "TabPage must exist within a TabControl");
Parent.AddPage(this);
base.OnInitialized();
}
}
//Tab Control
<div style="display:flex;">
<ul style="display:flex;">
@foreach (TabPage tabPage in Pages)
{
<li style="margin-right:20px; @GetActiveClass(tabPage); list-style:none; " @onclick=@(() => ActivatePage(tabPage))>@tabPage.Text</li>
}
</ul>
</div>
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
public TabPage ActivePage { get; set; }
public List<TabPage> Pages { get; } = new List<TabPage>();
public void AddPage(TabPage tabPage)
{
Pages.Add(tabPage);
if (Pages.Count == 1)
ActivePage = tabPage;
StateHasChanged();
}
public string GetActiveClass(TabPage page)
{
return page == ActivePage ? "border-bottom: 2px solid #263238;" : "";
}
public void ActivatePage(TabPage page)
{
System.Console.WriteLine($"Activating Page: {page.Text} - {page.Id}.");
ActivePage = page;
}
}