From 309eeada03b8a0eaa0768fbcfa910d644f56c3ff Mon Sep 17 00:00:00 2001 From: masumulu28 Date: Fri, 24 Mar 2023 15:43:43 +0300 Subject: [PATCH] grouped menu feature refactored document updated test updated --- docs/en/UI/Angular/Modifying-the-Menu.md | 23 ++++------- .../packages/core/src/lib/core.module.ts | 2 +- .../packages/core/src/lib/models/common.ts | 13 ++---- .../core/src/lib/services/routes.service.ts | 40 ++++++------------- .../core/src/lib/tests/routes.service.spec.ts | 32 ++++----------- .../core/src/lib/tokens/others-group.token.ts | 3 +- 6 files changed, 33 insertions(+), 80 deletions(-) diff --git a/docs/en/UI/Angular/Modifying-the-Menu.md b/docs/en/UI/Angular/Modifying-the-Menu.md index b3ab040910..b084f75781 100644 --- a/docs/en/UI/Angular/Modifying-the-Menu.md +++ b/docs/en/UI/Angular/Modifying-the-Menu.md @@ -96,19 +96,12 @@ import { ABP } from '@abp/ng.core'; type GroupType = ABP.Group; -function configureRoutes(routes: RoutesService) { - const myGroup: GroupType = { key:'groupKey', text:'GroupName' }; - +function configureRoutes(routes: RoutesService) { return () => { routes.add([ { - path: '/your-path', - name: 'Your navigation', - requiredPolicy: 'permission key here', - order: 101, - iconClass: 'fas fa-question-circle', - layout: eLayoutType.application, - group: myGroup + //etc.. + group: 'ModuleName::GroupName' }, { path: '/your-path/child', @@ -138,11 +131,11 @@ export class AppComponent { ``` ...and then in app.module.ts... - - `groupedVisible` method will return `Others` group for ungrouped items, we can define `key` and `text` via `OTHERS_GROUP` injection token for this group + - `groupedVisible` method will return `Others` group for ungrouped items, Default key is `AbpUi::OthersGroup` we can change this `key` via `OTHERS_GROUP` injection token ```js import { NgModule } from '@angular/core'; -import { ABP, OTHERS_GROUP } from '@abp/ng.core'; +import { OTHERS_GROUP } from '@abp/ng.core'; import { APP_ROUTE_PROVIDER } from './route.provider'; @NgModule({ @@ -150,7 +143,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider'; APP_ROUTE_PROVIDER, { provide: OTHERS_GROUP, - useValue: { key: 1, text: 'MyOthersGroup' } as ABP.Group, + useValue: 'ModuleName::MyOthersGroupKey', }, ], // imports, declarations, and bootstrap @@ -168,9 +161,7 @@ Here is what every property works as: - `iconClass` is the class of the `i` tag, which is placed to the left of the navigation label. - `layout` defines in which layout the route will be loaded. (default: `eLayoutType.empty`) - `invisible` makes the item invisible in the menu. (default: `false`) -- `group` is an optional property that is used to group together related routes in an application. It's an object and it have 2 property - - `key` is a generic type property that we use for gather items in same group. (default type: `string`) - - `text` is the display name on menu +- `group` is an optional property that is used to group together related routes in an application. (type: `string`, default: `AbpUi::OthersGroup`) ### Via `routes` Property in `AppRoutingModule` diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 0b3747455a..818a464124 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -179,7 +179,7 @@ export class CoreModule { }, { provide: OTHERS_GROUP, - useValue: options.othersGroup || { key: 'others', text: '::Others' } as ABP.Group, + useValue: options.othersGroup || 'AbpUi::OthersGroup', }, IncludeLocalizationResourcesProvider, ], diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 13d7d8bdc2..c20c6849ed 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -13,7 +13,7 @@ export namespace ABP { sendNullsAsQueryParam?: boolean; tenantKey?: string; localizations?: Localization[]; - othersGroup?: Group; + othersGroup?: string; } export interface Child { @@ -68,20 +68,15 @@ export namespace ABP { invisible?: boolean; } - export interface Group { - key: TKey; - text: string; - } - export interface Route extends Nav { path?: string; layout?: eLayoutType; iconClass?: string; - group?: Group; + group?: string; } - export interface RouteGroup { - group: Group; + export interface RouteGroup { + group: string; items: TreeNode[]; } diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index d36228891e..74b76bb40d 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector, Inject, Optional, OnDestroy } from '@angular/core'; +import { Injectable, Injector, Inject, OnDestroy} from '@angular/core'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { ABP } from '../models/common'; import { OTHERS_GROUP } from '../tokens'; @@ -182,34 +182,20 @@ export abstract class AbstractNavTreeService @Injectable({ providedIn: 'root' }) export class RoutesService extends AbstractNavTreeService { - constructor( - injector: Injector, - @Optional() @Inject(OTHERS_GROUP) private readonly othersGroup: ABP.Group, - ) { + constructor(injector: Injector, @Inject(OTHERS_GROUP) private readonly othersGroup: string) { super(injector); } - get groupedVisible(): ABP.RouteGroup[] | undefined { - const groupTree = this.visible.filter(node => node.group); - if (groupTree.length < 1) return; - - const map = new Map[]>(groupTree?.map(node => [node.group, []])); - const otherGroup = this.othersGroup; - map.set(otherGroup, []); - - for (const node of this.visible) { - const { path, children, group } = node; - - if (!group && (children?.length > 0 || path)) { - map.get(otherGroup)?.push(node); - } else if (group) { - map.get(group)?.push(node); - } - } - - return Array.from(map.entries()).map(([group, nodes]) => ({ - group, - items: nodes, - })); + const hasGroup = this.visible.some(node => !!node.group); + if (!hasGroup) return; + + const groups = this.visible.reduce((acc, node) => { + const groupName = node.group ?? this.othersGroup; + acc[groupName] ||= []; + acc[groupName].push(node); + return acc; + }, {} as Record[]>); + + return Object.entries(groups).map(([group, items]) => ({ group, items })); } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts index a45b4d6d36..d4871108a3 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts @@ -1,29 +1,26 @@ import { Subject } from 'rxjs'; import { take } from 'rxjs/operators'; -import { ABP } from '../models'; import { RoutesService } from '../services/routes.service'; import { DummyInjector } from './utils/common.utils'; import { mockPermissionService } from './utils/permission-service.spec.utils'; const updateStream$ = new Subject(); -type GroupType = ABP.Group; - export const mockRoutesService = (injectorPayload = {} as { [key: string]: any }) => { const injector = new DummyInjector({ PermissionService: mockPermissionService(), ConfigStateService: { createOnUpdateStream: () => updateStream$ }, ...injectorPayload, }); - const othersGroupToken: ABP.Group = { key: 1, text: 'Others' }; - return new RoutesService(injector, othersGroupToken); + return new RoutesService(injector, 'OthersGroup'); }; describe('Routes Service', () => { let service: RoutesService; - const fooGroup: GroupType = { key: 'foo', text: 'FooGroup' }; - const barGroup: GroupType = { key: 'bar', text: 'BarGroup' }; + const fooGroup = 'FooGroup'; + const barGroup = 'BarGroup'; + const othersGroup = 'OthersGroup'; const routes = [ { path: '/foo', name: 'foo' }, @@ -75,18 +72,6 @@ describe('Routes Service', () => { }); }); - describe('#addGroup', () => { - it('should have routes with and without group', async () => { - service.add(groupedRoutes); - - const grouped = service.visible.filter(f => f.group); - const unGrouped = service.visible.filter(f => !f.group); - - expect(grouped.length).toBe(3); - expect(unGrouped.length).toBe(1); - }); - }); - describe('#groupedVisible', () => { it('should have groups and items', async () => { service.add(groupedRoutes); @@ -95,18 +80,15 @@ describe('Routes Service', () => { expect(tree.length).toBe(3); - expect(tree[0].group.key).toBe('foo'); - expect(tree[0].group.text).toBe('FooGroup'); + expect(tree[0].group).toBe('FooGroup'); expect(tree[0].items[0].name).toBe('foo'); expect(tree[0].items[0].children[0].name).toBe('y'); - expect(tree[1].group.key).toBe('bar'); - expect(tree[1].group.text).toBe('BarGroup'); + expect(tree[1].group).toBe('BarGroup'); expect(tree[1].items[0].name).toBe('bar'); expect(tree[1].items[1].name).toBe('baz'); - expect(tree[2].group.key).toBe(1); - expect(tree[2].group.text).toBe('Others'); + expect(tree[2].group).toBe(othersGroup); expect(tree[2].items[0].name).toBe('z'); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/others-group.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/others-group.token.ts index 396bb3db13..23d7d65b92 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/others-group.token.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/others-group.token.ts @@ -1,4 +1,3 @@ import { InjectionToken } from '@angular/core'; -import { ABP } from '../models'; -export const OTHERS_GROUP = new InjectionToken('OTHERS_GROUP'); +export const OTHERS_GROUP = new InjectionToken('OTHERS_GROUP');