0

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;
    }
}
Chris
  • 43
  • 5
  • Post the code here – agua from mars Jul 29 '20 at 16:39
  • I have the same issue but instead if am just trying to make tabs either control tabs visability or change tabs titles. I have not found a working solution yet – Seer Feb 16 '21 at 23:23
  • A tricky gotcha with foreach and lambdas. The data in `Remove(data)` is always the last element. In the for loop set a local `var d=data` and `Remove(d) `. – Ray Feb 17 '21 at 07:10
  • See https://stackoverflow.com/questions/271440/captured-variable-in-a-loop-in-c-sharp – Ray Feb 17 '21 at 07:20

0 Answers0