Open Closed

Blazor dynamically loaded menu #10642


User avatar
0
LumeteDev1 created

Hi,

I need to add ItemMenu and SubItemMenu dynamically through AppService.

The main menu documentsMenu appears only collapsed and all the main menus (e.g., SaaS, Administration, etc.) are only collapsed and cannot be expanded.

The generated HTML seems correct, however, the SubItemMenus exist.

How can I resolve this?

Thanks

This is code:

    // Documenti - albero con classi documentali come foglie
    var documentsMenu = new ApplicationMenuItem(
        Suite4SolutionsMenus.Documents,
        l["Menu:Documents"],
        icon: "fa fa-paperclip");

    try
    {
        if (await context.IsGrantedAsync(Suite4SolutionsPermissions.Documents.Default))
        {
            var documentMenuState = context.ServiceProvider.GetRequiredService<DocumentMenuState>();
            await documentMenuState.EnsureLoadedAsync();

            foreach (var dc in documentMenuState.DocumentClasses)
            {
                documentsMenu.AddItem(new ApplicationMenuItem(
                    Suite4SolutionsMenus.Documents + "." + dc.Id.ToString("N"),
                    dc.Name,
                    url: $"/documents/{dc.Id}",
                    icon: "fa fa-file-alt",
                    requiredPermissionName: Suite4SolutionsPermissions.Documents.Default));
            }
        }
    }
    catch
    {
        // Se la chiamata API fallisce, mostra solo il menù padre
    }

    context.Menu.AddItem(documentsMenu);
Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

2 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP Blazor (including WASM) the expand/collapse behavior of parent menu items is purely client-side (handled by the theme’s Blazor/JS code). If the menu items are added/changed after the side menu has already been initialized (e.g., after an async call like EnsureLoadedAsync()), the DOM gets updated but the theme’s “bind click handlers / initialize side-menu” step won’t automatically re-run, so parent items stay collapsed and not expandable.

    • Fix: Re-initialize the LeptonX side menu after you dynamically build the menu.

      1. Create a small JS function in your Blazor WASM wwwroot/index.html (or a JS file included by it) to re-run LeptonX’s side-menu initializer if it exists:
    <script>
           window.reinitLeptonXSideMenu = function () {
             const fn = window.leptonx
               && window.leptonx.init
               && window.leptonx.init.initializers
               && window.leptonx.init.initializers.get('initSideMenu');
    
             if (fn) {
               fn(); // rebind expand/collapse handlers
             }
           };
         </script>
    
    1. Call that JS method after the menu is loaded/changed on the client. The clean ABP way is to do it from a component that runs after render (e.g., your main layout or a component that hosts the menu) after the menu state changes:
    @inject IJSRuntime JS
    
         protected override async Task OnAfterRenderAsync(bool firstRender)
         {
             // If your menu can change later (async), you can also call this whenever it changes.
             await JS.InvokeVoidAsync("reinitLeptonXSideMenu");
         }
    
     If you have a specific place where you know the menu has just been updated (e.g., after your state/service finishes loading), call it there too.
    
    • Important: Do not try to build menus from an AppService at runtime expecting the UI to “just expand” without re-init. ABP’s IMenuContributor is designed for building the menu, but the theme initialization still must see the final DOM to wire expand/collapse.

    • Your ApplicationMenuItem code is fine. The symptom you describe (HTML contains sub-items but groups can’t expand) matches exactly “menu JS init ran before items existed”.

    Links (verified):

    • https://abp.io/docs/latest/framework/ui/blazor/navigation-menu

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks for reporting this. The "collapse but not expanding" behavior you see (and the fact that it also breaks unrelated menus like SaaS / Administration) is caused by a timing issue in LeptonX 5.3.x sidebar initialization, triggered when an IMenuContributor awaits a real HTTP call.

    What's happening

    LeptonX's SideMenuLayout runs initLeptonX (which wires up the click handlers for sidebar items) once, on its first render. Your contributor awaits documentMenuState.EnsureLoadedAsync(), which goes over HTTP. While that HTTP is in flight, the MainMenu child component hasn't rendered yet, so the menu DOM (<a class="lpx-menu-item"> for each main menu) doesn't exist when LeptonX scans the page. By the time your HTTP returns and the menu items appear, nothing re-binds the click handlers — so every main menu (yours and the built-in ones) ends up collapsed and unclickable.

    We've already fixed this on the LeptonX side: BaseSideMenu now uses a MutationObserver on #lpx-sidebar plus an idempotent data-lpx-bound marker, so menu items added after the first init get bound automatically. The fix is referenced from a related ticket https://abp.io/support/questions/10622 and will ship in the next LeptonX 5.3.x patch.

    Workaround until that patch is out

    Drop a small JS file into your Blazor.Client wwwroot/, e.g. lpx-sidebar-rebind-polyfill.js:

    (function () {
        'use strict';
        var DONE_ATTR = 'data-lpx-polyfill-rebound';
    
        function rebind() {
            var sidebar = document.querySelector('#lpx-sidebar');
            if (!sidebar || sidebar.hasAttribute(DONE_ATTR)) return false;
            if (!sidebar.querySelector('.outer-menu-item > .lpx-menu-item')) return false;
    
            sidebar.setAttribute(DONE_ATTR, '');
            var initFn = window.leptonx
                && window.leptonx.init
                && window.leptonx.init.initializers
                && typeof window.leptonx.init.initializers.get === 'function'
                && window.leptonx.init.initializers.get('initSideMenu');
            if (typeof initFn === 'function') { initFn(); return true; }
            return false;
        }
    
        function start() {
            if (rebind()) return;
            var observer = new MutationObserver(function () {
                if (rebind()) observer.disconnect();
            });
            observer.observe(document.body, { childList: true, subtree: true });
            setTimeout(function () { observer.disconnect(); }, 30000);
        }
    
        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
        else start();
    })();
    

    Reference it in wwwroot/index.html after the LeptonX bundle:

    <script src="lpx-sidebar-rebind-polyfill.js"></script>
    

    It watches for the sidebar menu DOM to appear, then triggers initSideMenu exactly once, so the click handlers land on the real menu items. The DONE_ATTR flag and the 30-second observer timeout keep it from double-binding on later navigations (which would otherwise cause one click to toggle twice on this LeptonX version).

    Two small unrelated suggestions on your contributor:

    • The empty catch { } swallows everything — consider Logger.LogException(ex) so you can actually see when the HTTP call fails.
    • You can drop requiredPermissionName from the child items; the parent already gates them via IsGrantedAsync, so re-checking on each child is just wasted work.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.