Enrico
Enrico

Reputation: 6266

Blazor and JavaScript functions not recognized

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?

Upvotes: 1

Views: 710

Answers (1)

Enrico
Enrico

Reputation: 6266

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

Upvotes: 1

Related Questions