1

In my Blazor project, I have a lot of tabs to display. I was looking for a solution and I found a nice JavaScript implementation with jQuery that is working for desktop and mobile. Here an example of the jQuery implementation

enter image description here

It is working quite smoothly, and it is exactly what I need. So, I started to create a component with Blazor to reuse it and share. I published my code with examples on GitHub.

The problem I face is related to CascadingValue. My component has a container called ScrollTabs and each single Tab. So, I can write in the application something like that

<ScrollTabs TabId="TabId1" OnTabChanged="OnTabChanged">
    <Tab Text="Tab 1" Value="Tab1">
        <h2>Content Tab 1</h2>
        <p>
            This is the content for the Tab 1. It is enabled.
        </p>
    </Tab>
</ScrollTabs>

The ScrollTabs container is quite simple

<CascadingValue Value="this">
    <div id="@TabId" class="scroll_tabs_theme_light">
        @foreach (Tab tabPage in Pages)
        {
            <span>
                @tabPage.Text
            </span>
        }
    </div>
    <div class="tab-content">
        @ChildContent
    </div>
</CascadingValue>

@code {
    private DotNetObjectReference<ScrollTabs>? dotNetHelper;

    [Parameter]
    public string? TabId { get; set; }

    [Parameter] public RenderFragment? ChildContent { get; set; }

    [Parameter] public EventCallback<Tab> OnTabChanged { get; set; }

    public Tab? ActivePage { get; set; }
    List<Tab> Pages = new List<Tab>();

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JSRuntime.InvokeVoidAsync("Helpers.setDotNetHelper", dotNetHelper);
        }

        // Setup the tabs
        await JSRuntime.InvokeVoidAsync("setup", TabId);
    }

    internal void AddPage(Tab tabPage)
    {
        Pages.Add(tabPage);
        if (Pages.Count == 1)
            ActivePage = tabPage;

        StateHasChanged();
    }
}

Then Tab is define like

@if (Parent.ActivePage == this)
{
    @ChildContent
}

@code {
    [CascadingParameter]
    public ScrollTabs? Parent { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public string? Text { get; set; }

    [Parameter]
    public string? Value { get; set; }

    [Parameter]
    public bool Enabled { get; set; } = true;

    protected override void OnInitialized()
    {
        if (Parent == null)
            throw new ArgumentNullException(nameof(Parent), "TabPage must exist within a TabControl");

        base.OnInitialized();
        Parent.AddPage(this);
    }
}

Now, if I run the code with the CascadingValue, the JavaScript has some issues because a div is created few times.

enter image description here

If I remove the CascadingValue, the CascadingParameter doesn't have a value and so it raises an error. But, if I add some vanilla HTML code, the tabs from the HTML is working as expected (in the following screenshot, the first gray space is the render of the Blazor component, the second one the HTML code)

enter image description here

How can I fix it?

1 Answer 1

1

I found the solution. I have to add the setup only in the firstRender.

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        dotNetHelper = DotNetObjectReference.Create(this);
        await JSRuntime.InvokeVoidAsync("Helpers.setDotNetHelper", dotNetHelper);

        // Setup the tabs
        await JSRuntime.InvokeVoidAsync("setup", TabId);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.