Open Closed

Need help with navigation and breadcrumbs not displaying correctly #8392


User avatar
0
dshapiro created
  • ABP Framework version: v8.3.0
  • UI Type: Angular
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes
  • Exception message and full stack trace: N/A
  • Steps to reproduce the issue:

In our Angular application we've defined our navigation items in src/app/route.provider.ts and defined our routes within src/app/app-routing.module.ts with additional child routes loaded from lazy-loaded modules referenced within it.

We're finding that items in the navigation menu that have sub-items don't stay open/expanded (as the out of the box Administration menu does) when visiting child routes. Possibly related, we note that the breadcrumbs rarely show the correct location either.

Are we missing something obvious here? Does this just not work with lazy-loaded routes?


11 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    ABP routes are lazy load, too; you can check it.

    https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/identity/src/lib/identity.module.ts#L68

    How do you defined your navigation items, could you show me the full steps?

  • User Avatar
    0
    dshapiro created

    It looks like we're missing the forLazy() call from our module import, but it seems that's something we need to manually create on our modules and just looking at your link, I don't understand how to create this (in explanation, I don't understand what's happening in the forChild() call).

    Here's an anonymized sample of how we've set things up (routes and module names changed to protect our client). Apologies if some of this is implemented strangely; our team is somewhat inexperienced with Angular.

    Would this all work if we implemented and used forLazy() on all of our routing modules that do lazy loading?

    /src/app/app-routing.module.ts

    import { authGuard, permissionGuard } from '@abp/ng.core';
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    
    const routes: Routes = [
      // ... Removed default ABP routes
    
      // Custom routes
      { 
        path: 'module1', 
        loadChildren: () => import('./modules/module1/module1.module').then(m => m.Module1),
      },
      {
        path: 'module2', 
        loadChildren: () => import('./modules/module2/module2.module').then(m => m.Module2),
      },
      {
        path: 'module3', 
        loadChildren: () => import('./modules/module3/module3.module').then(m => m.Module3),
      },
      {
        path: 'module4', 
        loadChildren: () => import('./modules/module4/module4.module').then(m => m.Module4),
      },
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes, { bindToComponentInputs: true })],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}  
    

    /src/app/modules/module1/module1-routing.module.ts

    import { NgModule } from '@angular/core';
    import { permissionGuard } from '@abp/ng.core';
    import { RouterModule, Routes } from '@angular/router';
    
    const routes: Routes = [
      {
        path: '',
        children: [
          {
            path: 'page1',
            loadChildren: () => import('./module1/page1.module').then(m => m.Page1Module), // Has own routing that exposes child routes for CRUD operations (list, edit, create, update)
          },
          {
            path: 'page2',
            loadChildren: () => import('./module1/page2.module').then(m => m.Page2Module), // Has own routing that exposes child routes for CRUD operations (list, edit, create, update)
            
          },
          // ... snip ...
        ]
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule]
    })
    export class Module1RoutingModule { }
    
    

    /src/app/route.provider.ts

    import { RoutesService, eLayoutType } from '@abp/ng.core';
    import { APP_INITIALIZER } from '@angular/core';
    
    export const APP_ROUTE_PROVIDER = [
      { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
    ];
    
    function configureRoutes(routes: RoutesService) {
      return () => {
        routes.add([
          {
            path: '/',
            name: '::Menu:Home',
            iconClass: 'fas fa-home',
            order: 1,
            layout: eLayoutType.application,
          },
          {
            path: '/dashboard',
            name: '::Menu:Dashboard',
            iconClass: 'fas fa-chart-line',
            order: 2,
            layout: eLayoutType.application,
            
          },
          {
            name: '::Menu:Module1',
            order: 3,
            iconClass: 'fa fa-gear',
            layout: eLayoutType.application,
          },
          {
            path: '/module1/page1',
            name: 'M1P1',                        
            parentName: '::Menu:Module1',
            order: 1,
            iconClass: 'fas fa-table',
            layout: eLayoutType.application,
          },
          {
            path: '/module1/page2',
            name: 'M1P2',
            parentName: '::Menu:Module1', 
            iconClass: 'fas fa-table',
            order: 2,
            layout: eLayoutType.application,
          },  
          // ... snip ...
        ]);
      };
    }
    
    
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    It looks like we're missing the forLazy() call from our module import, but it seems that's something we need to manually create on our modules and just looking at your link, I don't understand how to create this (in explanation, I don't understand what's happening in the forChild() call).

    Not related to this, forlazy is used to configure some services.

    Could you please share a test project with me? i will check it. thanks shiwei.liang@volosoft.com

  • User Avatar
    0
    dshapiro created

    Sorry for the delay, I've been quite busy with competing priorities. I'll get you this test project as soon as I can, but wanted to check in and ensure this ticket doesn't get closed before I get a chance.

  • User Avatar
    0
    dshapiro created

    Hi Liang Shiwei,

    I tried to email you, but it was rejected by the mail server. I've sent the file as a Google Drive share instead.

    Thanks

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    This is not about lazy load.

    This is because of your route path,they do not match

  • User Avatar
    0
    dshapiro created

    Ah, that makes complete sense. Thank you!

    I wonder, though: is there any ability to highlight the menu item for child routes? As in my example, we usually have /list and /detail child routes. It would be nice to be able to set a menu item "Page 1" with a path of "/module1/page1" and have it be marked as active if we're at either "/module1/page1/list" or "/module1/page1/detail".

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    You may consider adding multi-level submenu

  • User Avatar
    0
    dshapiro created

    We would like to avoid that as it's too granular for the menu and the detail page has a dynamic url (based on selection from the list page). Any options for this work as I described?

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Ah, that makes complete sense. Thank you!

    I wonder, though: is there any ability to highlight the menu item for child routes? As in my example, we usually have /list and /detail child routes. It would be nice to be able to set a menu item "Page 1" with a path of "/module1/page1" and have it be marked as active if we're at either "/module1/page1/list" or "/module1/page1/detail".

    Hello, you can manage the breadcrumb value by adding the breadcrumbText attribute to your route configurations like this

    {
      path: '/module1/page1',
      name: 'M1P1',
      parentName: '::Menu:Module1',
      order: 1,
      iconClass: 'fas fa-table',
      layout: eLayoutType.application,
      breadcrumbText: 'Custom Breadcrumb',
    }
    

    However, the highlight you mentioned is also managed within this route config for the time being. So, you will need to add them here.

  • User Avatar
    0
    dshapiro created

    Sorry, I'm not quite following your meaning. I'm less concerned about the breadcrumbs and more so wondering if I could define a navigation item that behaves as a sort of "catch-all":

    *app-routing.module.ts*
    
    {
       path: '/module1/page1/**', // Something to denote a wildcard/catchall
       name: 'M1P1',
       // etc,
       isActive: (activatedRoute, itemConfig /* this node */) => isPathOrChild(itemConfig.path, activatedRoute); // Or something like this
    }
    

    And have this item selected for any of: /module1/page1 /module1/page1/list /module1/page1/details

Made with ❤️ on ABP v9.1.0-preview. Updated on December 13, 2024, 06:09