MudBlazor / Templates

Ready to use Blazor Templates in different styles and layout with all the basic setup already done for MudBlazor.
MIT License
745 stars 165 forks source link

Unable to toggle drawer containing `MudNavMenu` when on `/Account` (SSR) pages. #478

Closed bitbound closed 1 month ago

bitbound commented 1 month ago

Out-of-the-box, it's not possible to toggle the drawer containing the MudNavMenu when you're on any of the account pages (using SSR). This is particularly problematic on mobile, since you're no longer able to navigate.

I see that the templates are using 0phois/MudBlazor.StaticInput for the SSR pages, but they don't have a component for MudIconButton or MudNavMenu. So I'm not sure if this issue belongs here as a bug, or in that repo as a feature request.

ScarletKuro commented 1 month ago

Works fine for me. You can see that I have MudNavMenu with MudNavLinks and that I'm on auth (/Account) pages and the navigation does work. I tested before everything: WASM, Server, Auth with per-page and global interactivity.

works

bitbound commented 1 month ago

Sorry, I wasn't clear. It's the MudIconButton that toggles the drawer.

https://github.com/user-attachments/assets/8314719d-66e8-4f43-88c7-1dc67248c2f0

ScarletKuro commented 1 month ago

Sorry, I wasn't clear. It's the MudIconButton that toggles the drawer.

That's the key part, the drawer and the nav menu are very different things.

This is expected for now and has been explained multiple times. When you are on static SSR pages, there is no interactivity, which means that any C# code will not execute. Our drawer functions only when there is interactivity, as the open state is managed by the C# code. I don't think 0phois/MudBlazor.StaticInput can do anything either.

There are only three ways to handle this:

  1. Use a drawer that is purely written in HTML and CSS.
  2. Theoretically, you could use JavaScript to change the class of the drawer from open to closed, but if I remember correctly, there could be other side effects.
  3. Create a Pull Request for a mini drawer in mobile view, featuring just a tiny sidebar with icons only. That's what we had in plans, but currently there is no ETA.
bitbound commented 1 month ago

This is expected for now and has been explained multiple times.

I wouldn't have created this issue had I seen anywhere that this is a known incompletion. I searched the issues and documentation, but didn't see anything specifically for this. Maybe I missed something, but I did look.

When you are on static SSR pages, there is no interactivity, which means that any C# code will not execute.

I understand how interactivity and render modes work. I understand why this isn't working.

This works in the default Blazor templates (again, don't need to explain the technical reasons why it works in them). So a reasonable person, even one who understands the technical reasons why it's not working, would expect them to work in these templates as well. A reasonable person would either consider this a bug or an oversight.

If you're frustrated from having to explain this (maybe I'm mistaken, but I kinda get that feeling), I suggest one or more of the following:

Anyhow, thank you for your work on MudBlazor. I've really been enjoying it for my own open-source stuff. Have a good day!

ScarletKuro commented 1 month ago

wouldn't have created this issue had I seen anywhere that this is a known incompletion. I searched the issues and documentation, but didn't see anything specifically for this. Maybe I missed something, but I did look.

https://github.com/MudBlazor/Templates/issues/461
https://github.com/MudBlazor/MudBlazor/issues/9523
https://github.com/MudBlazor/MudBlazor/issues/9743
+Discord
+General GitHub discussion about WebApp.

I'm not frustrated, and I apologize if it sounds passively aggressive. The reason we keep mentioning that this has been discussed multiple times is that, when .NET 8 was in its release candidate stage, we publicly stated in all channels that we would not support static SSR. MudBlazor was developed way before static SSR was introduced, and adopting it would require extensive rewriting. This also goes against our principle of not using JavaScript unless absolutely necessary. At this point, MudBlazor would essentially just wrap some JavaScript libraries into Blazor components, which was not our initial intention. This template represents what we could squeeze out from MudBlazor with static SSR, it's not perfect, but it's significantly better than what we had in the 1.0.0 template version.

I suggest one or more of the following:

  • Add a section to the README that lists features, such as this one, that are incomplete or non-functioning.
  • Add a pinned issue containing known bugs and incomplete features.
  • Add comments in the template code next to the incomplete or non-functioning components.

Thank you for the feedback. I will work on improving the documentation.

bitbound commented 1 month ago

For anyone who arrives here and would like an example of a potential solution, here's what I did. This assumes you're using the default value for MudDrawer.Breakpoint. It's working for me, minus the missing backdrop. It will handle both desktop and mobile views, and it handles transitions between the breakpoint when resizing the window.

I'm sure edge cases will pop up at some point. 😄

MainLayout.razor:


@inject NavigationManager NavManager

@* other content *@

      @* Added id to toggle button. *@
      <MudIconButton id="nav-drawer-toggle-button"
                     Icon="@Icons.Material.Filled.Menu"
                     Color="Color.Inherit"
                     Edge="Edge.Start"
                     OnClick="@(_ => DrawerToggle())" />

@* other content *@

    @* Added id to drawer. *@
    <MudDrawer @bind-Open="_drawerOpen"
               id="nav-drawer"
               ClipMode="DrawerClipMode.Always"
               Elevation="2">

@* other content *@

  @*  Add JavaScript if we're on an Identity page. *@
  @if (_isIdentityPage)
  {
    <script src="/Layout/MainLayout.razor.js"></script>
  }

@code {
  private bool _isIdentityPage;

 // other content

  protected override async Task OnInitializedAsync()
  {
    _isIdentityPage =
      Uri.TryCreate(NavManager.Uri, UriKind.Absolute, out var currentUri) &&
      currentUri.PathAndQuery.StartsWith("/Account");

     // other content
  }
}

MainLayout.razor.js (new file in same folder as MainLayout.razor):

/** @type {HTMLButtonElement} */
const toggleButton = document.getElementById("nav-drawer-toggle-button");
const navDrawer = document.getElementById("nav-drawer");
const drawerParent = navDrawer.parentElement;
const desktopQuery = window.matchMedia("(min-width: 960px)");

desktopQuery.addEventListener("change", ev => {
  if (ev.matches) {
    navDrawer.classList.add("mud-drawer-md");
    navDrawer.classList.remove("mud-drawer--closed");
    navDrawer.classList.add("mud-drawer--open");
    drawerParent.classList.remove("mud-drawer-closed-responsive-md-left");
    drawerParent.classList.add("mud-drawer-open-responsive-md-left");
  }
});

toggleButton.addEventListener("click", () => {
  const isDesktopWidth = desktopQuery.matches;
  const hasBreakpoint = navDrawer.classList.contains("mud-drawer-md");

  if (!isDesktopWidth && hasBreakpoint) {
      navDrawer.classList.remove("mud-drawer-md");
    return;
  }

  if (isDesktopWidth && !hasBreakpoint) {
    navDrawer.classList.add("mud-drawer-md");
  }

  navDrawer.classList.toggle("mud-drawer--open");
  navDrawer.classList.toggle("mud-drawer--closed");
  drawerParent.classList.toggle("mud-drawer-closed-responsive-md-left");
  drawerParent.classList.toggle("mud-drawer-open-responsive-md-left");
});

https://github.com/user-attachments/assets/a57528a8-7b05-4fa4-8e9f-008dc45a6da2

andrewlongdotcom commented 1 month ago

Thank you so much @bitbound for sharing your solution.

nhwilly commented 1 week ago

I just did a check to see how the FluentUIBlazor folks did it. Sure enough, there's hidden check box (not exactly sure what that's about) and a Javascript file specific to the nav menu to handle it.

Thanks for this answer!

nhwilly commented 1 week ago

This is working great. Thank you so much. I'm just wondering what other tiny landmines are lurking. If it hadn't been for this, I'd be looking at other UI packages. I like MB, but I picked it just so I wouldn't have to tangle too much with JS.

"And for anyone else who arrives here..."

In the script that's shown, the folder for the MainLayout is not where the template puts it. All you have to do is modify this: <script src="/Components/Layout/MainLayout.razor.js"></script>

to this: <script src="/Layout/MainLayout.razor.js"></script>

My MainLayout is in the client project, not the main project.

bitbound commented 1 week ago

@nhwilly Thanks for pointing this out. MainLayout is in the client project for me too, but I forgot that I had moved Layout and Pages folders under a new Components folder.

I updated my example with the correct path. :)