Search code examples
c#blazorblazor-server-sideblazor-webassembly

MainLayout and Sidebar rendermode in Blazor


My application is built with .NET 8 in Blazor. The configuration in the Program.cs is like this:

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();

I want to have a nice sidebar for it. I created a SidebarComponent that generates the sidebar quite nicely. This is the code of the component

@inject NavigationManager _navigationManager
@rendermode InteractiveAuto

<aside id="sidebar" class="sidebar break-point-sm has-bg-image @(_isCollapsed ? "collapsed" : "") @(_isToggled ? "toggled" : "")">
    <a @onclick="BtnCollapseClicked" id="btn-collapse" class="sidebar-collapser"><i class="ri-arrow-left-s-line"></i></a>
    <div class="image-wrapper">
    </div>
    <div class="sidebar-layout">
        <div class="sidebar-header">
            <div class="pro-sidebar-logo">
                <div>P</div>
                <h5>Pro Sidebar</h5>
            </div>
        </div>
        <div class="sidebar-content">
            <nav class="menu open-current-submenu">
                <ul>
                    <li class="menu-header"><span>BLAZOR</span></li>

                    @foreach (var menuItem in standardItems)
                    {
                        <MenuItemComponent MenuItem="@menuItem" MenuItemClickCallback="MenuItemClick" />
                    }
                </ul>
            </nav>
        </div>
    </div>
</aside>

@code {
    private bool _isCollapsed { get; set; } = false;
    private bool _isToggled { get; set; } = false;

    List<MenuItem> standardItems = SidebarData.GetStandardMenuItems();
    List<MenuItem> generalMenuItems = SidebarData.GetGeneralMenuItems();

    public void BtnToggleClicked()
    {
        _isToggled = !_isToggled;
        StateHasChanged();
    }

    public void MouseClickedInOverlay()
    {
        if (_isToggled)
            _isToggled = false;
        generalMenuItems.ForEach(x => x.IsOpened = false);
        StateHasChanged();
    }

   // Omitted
}

Then in the MainLayout, I added

<div class="layout has-sidebar fixed-sidebar fixed-header">
    <SidebarComponent @ref="sideBarComponent"/>

    <div @onclick="@(e => sideBarComponent?.MouseClickedInOverlay())" id="overlay" class="overlay"></div>
    <div class="layout">
    <!-- Omitted -->
    </div>
</div>

When the application starts, I get this error:

System.InvalidCastException: 'Unable to cast object of type 'Microsoft.AspNetCore.Components.Endpoints.SSRRenderModeBoundary' to type 'HypnoPlatform.Client.Pages.Shared.Sidebar.SidebarComponent'.'

enter image description here

How can I call methods from the MainLayout to the SidebarComponent?


Solution

  • I recall stumbling into this previously. I think the issue is that the line:

    <SidebarComponent @ref="sideBarComponent"/>
    

    is, unintuitively, trying to assign an SSRRenderModeBoundary to the variable sideBarComponent, instead of a SidebarComponent. Something to do with the framework wrapping the component with SSRRenderModeBounday when using InteractiveAuto.

    I never really got to the bottom of it, but using a shared service to communicate was my solution. Something like:

    public class SidebarStateService
    {
        public event Action? OnOverlayClicked;
    
        public void OverlayClicked()
        {
            OnOverlayClicked?.Invoke();
        }
    }
    

    Then inject this into both your MainLayout and Sidebar.

    Call OverlayClicked from MainLayout and hookup to OnOverlayClicked in your Sidebar.,