Reputation: 6266
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
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.
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)
How can I fix it?
Upvotes: 1
Views: 710
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