David-Moreira / BlazorXTabs

BlazorXTabs is an extended tabs component library providing various tab features for Blazor!
https://david-moreira.github.io/BlazorXTabs/
MIT License
34 stars 10 forks source link

Child component cannot be referenced OnActiveTabChanged #27

Closed nbiada closed 3 years ago

nbiada commented 3 years ago

If a component has a ref attribute in the OnActiveTabChanged the reference is NULL.

Example

<XTabs @ref="navTabs" OnActiveTabChanged="TabChanged"> 
    <XTab Title="First Tab"> 
    Hello 
  </XTab> 
  <XTab Title="Second Tab"> 
    <OtherComponent @ref="otherComp" /> 
  </XTab> 
</XTabs> 

...

@code {
  private async Task TabChanged(XTab tab) 
  { 
     SelectedTab = tab; 

     if (SelectedTab.Title == "Second Tab") {
       otherComp.SomeMethod();   //   <== ERROR, otherComp is NULL
    }
}

as a workaround I need to wait the end of the subcomponent rendering, because the reference is NULL on first cycle:

@code {
  private async Task TabChanged(XTab tab) 
  { 
     SelectedTab = tab; 

    await navTabs.NotifyStateHasChangedAsync();
    await Task.Delay(20);

    if (SelectedTab.Title == "Second Tab") {
       otherComp.SomeMethod();   //   <== call method
    }
}

and now it works as expected without errors.

David-Moreira commented 3 years ago

Hello @nbiada By default XTabs only renders each tab content once the tab has changed. Meaning only one tab content will be rendered at once. Which means the moment when you change tabs, the tab content is still in the process of starting it's render pipeline.

You can override this behaviour and have all tabs render at all times which will fix your issue by setting the RenderModeparameter to: BlazorXTabs.Configuration.RenderMode.Full

<XTabs @ref="navTabs" OnActiveTabChanged="TabChanged" RenderMode="BlazorXTabs.Configuration.RenderMode.Full"> 
    <XTab Title="First Tab"> 
    Hello 
  </XTab> 
  <XTab Title="Second Tab"> 
    <OtherComponent @ref="otherComp" />
  </XTab> 
</XTabs> 

Another way, while still using the default behaviour might be to check if the logic your calling in SomeMethod, could actually be called inside the components livecycle methods. Since the component only renders when the tab is set to active, the OnAfterRenderAsynclivecycle method is called with the firstRenderset to true each time, which enables you to run logic only once each time that tab is set to active.

OtherComponent.razor

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        SomeMethod();
    }
    await base.OnAfterRenderAsync(firstRender);
}

Let me know if this helps!

github-actions[bot] commented 3 years ago

Stale issue message