Open Closed

Angular UI: Creating navigation menu from Child modules #112


User avatar
0
rbarbosa created

Hello

Following the examples from IdentityConfig I have created 2 modules: StockTypesConfig: contains the service that will add a new menu item and a new child to that menu item StockTypes: has the components and routes

StockTypesConfigModule is loaded on app.module with the other ConfigModules, it is virtually identical to IdentityConfigModule:

StockTypesConfigModule:

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { noop } from '@abp/ng.core';
import { StockTypesConfigService } from './stock-types-config.service';


@NgModule({
  providers: [{provide: APP_INITIALIZER, deps:[StockTypesConfigService], useFactory: noop, multi: true}],
})
export class StockTypesConfigModule { }

StockTypesConfigService:

import { Injectable } from '@angular/core';
import { addAbpRoutes, eLayoutType } from '@abp/ng.core';

@Injectable({
  providedIn: 'root'
})
export class StockTypesConfigService {

  constructor() { 

    addAbpRoutes([
      {
        name: 'Inventory',
        path: '',
        order: 1,
        wrapper: true
      },

      {
        name: 'Stock Types',
        path: 'inventory/stock-types',
        order: 2,
        parentName: 'Inventory',
        layout: eLayoutType.application
      },
    ])
  }
}

this method does seem to create the children the same way the administration menu seems to handle it in identity-config.service.ts also the url seems to be incorrect: http://localhost:4200/inventory/stock-types/undefined

Any ideas what else I could be missing to achieve the parent/child menu structure?


5 Answer(s)
  • User Avatar
    0
    Mehmet created

    Hi @rbarbosa

    Can you share your app.routing.module.ts content to us?

  • User Avatar
    0
    rbarbosa created
    import {ABP, AuthGuard, PermissionGuard} from '@abp/ng.core';
    import {NgModule} from '@angular/core';
    import {RouterModule, Routes} from '@angular/router';
    import {ApplicationLayoutComponent} from "@volo/abp.ng.theme.lepton";
    
    const routes: Routes = [
      {
        path: '',
        loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
        data: {
          routes: {
            name: '::Menu:Home',
            iconClass: 'fa fa-home'
          } as ABP.Route
        }
      },
      { 
        path: 'inventory',
        loadChildren: () => import('./inventory/inventory.module').then(m => m.InventoryModule) 
      },
      {
        path: 'identity',
        loadChildren: () => import('@volo/abp.ng.identity').then(m => m.IdentityModule)
      },
      {
        path: 'account',
        loadChildren: () => import('@volo/abp.ng.account').then(m => m.AccountModule)
      },
      // the rest of @volo modules:  @volo/abp.ng.language-management @volo/abp.ng.saas
      // @volo/abp.ng.audit-logging @volo/abp.ng.identity-server
       {
        path: 'setting-management',
        loadChildren: () => import('@abp/ng.setting-management').then(m => m.SettingManagementModule)
      },
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule {
    }
    

    and Inventory Loads Stock Types:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    
    const routes: Routes = [
      { path: '', redirectTo: 'stock-types' }, 
      { path: 'stock-types', loadChildren: () => import('./stock-types/stock-types.module').then(m => m.StockTypesModule) }];
      
      @NgModule({
        imports: [RouterModule.forChild(routes)],
        exports: [RouterModule]
      })
      export class InventoryRoutingModule { }
      
    

    I started playing around and found that there is a ConfigStateService.dispatchAddRoute Method so i changed my StockTypeModuleConfigModule to do this instead:

    import { Injectable } from '@angular/core';
    import { addAbpRoutes, eLayoutType, AddRoute } from '@abp/ng.core';
    import { ConfigStateService } from '@abp/ng.core';
    
    @Injectable({
      providedIn: 'root'
    })
    export class StockTypesConfigService {
    
      constructor(private config: ConfigStateService) { 
    
        this.config.dispatchAddRoute(
          {
            name: 'Inventory',
            path: '',
            order: 1
          }
        )
        this.config.dispatchAddRoute(
          {
            name: 'Stock Types',
            path: 'inventory/stock-types',
            order: 2,
            parentName: 'Inventory',
            layout: eLayoutType.application
          }    
        )
      }
    }
    

    which leads to the route being added before init

    so I changed the menu to be patched after the first tick: stock-types-config.service.ts

    import { Injectable } from '@angular/core';
    import { addAbpRoutes, eLayoutType, AddRoute } from '@abp/ng.core';
    import { ConfigStateService } from '@abp/ng.core';
    
    @Injectable({
      providedIn: 'root'
    })
    export class StockTypesConfigService {
    
      constructor(private config: ConfigStateService) { 
        setTimeout(() => this.addRoutes());
      }
    
      addRoutes():void{
        this.config.dispatchAddRoute(
          {
            name: 'Inventory',
            path: '',
            order: 1
          }
        )
        this.config.dispatchAddRoute(
          {
            name: 'Stock Types',
            path: 'inventory/stock-types',
            order: 2,
            parentName: 'Inventory',
            layout: eLayoutType.application
          }
        )
      }
      
    }
    
    

    this works!

    but i would love to know the 'proper' way to define routes from sub-modules

  • User Avatar
    0
    Mehmet created

    The path property accepts one level route like below:

    addAbpRoutes([
      {
        name: 'Inventory',
        path: '',
        order: 1,
        wrapper: true,
      },
      {
        name: 'Stock Types',
        path: 'inventory',
        order: 2,
        parentName: 'Inventory',
        layout: eLayoutType.application,
        children: [{ name: 'Stock Types', path: 'stock-types' }],
      },
    ]);
    

    But it seems a bug. We will be fixed.

    The proper way is to add routes by using the addAbpRoutes function. You use the addAbpRoutes function after the bug is resolved.

    By the way, adding routes by using the dispatchAddRoute method, not a bad solution.

    Thanks for reporting.

  • User Avatar
    0
    rbarbosa created

    Thank you, will update my sources when the packages receive the bugfix.

    On the same vein, if I have this scenario:

    Main application Module

    • SubModuleA (SubModuleAConfigService)
    • SubModuleB (SubModuleBConfigService)

    and the menu structure is somewhat like this: Main menu -CategoryA --SubModuleA --SubModuleB

    From a submodule perspective I should be defining the 'CategoryA' menu in all modules as they do not depend on each other, but sadly this creates duplicate CategoryA menu listings, would it be possible to 'patch' the menu structure rather than adding a clone? Taken from identity-config.service.ts if I remove this module, all other modules depending on the administration menu structure will fail, as they are not defining it.

  • User Avatar
    0
    Mehmet created

    For now, wrappers should be defined by just one module. We'll change the wrapper structure. Each module will be defined own wrapper category without duplication.

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