diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index c07f11580f..5455187086 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,13 +1,14 @@ -import { Component, OnDestroy, Type } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router'; +import { Component, Injector, OnDestroy, Type } from '@angular/core'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngxs/store'; -import snq from 'snq'; import { eLayoutType } from '../enums/common'; -import { ABP } from '../models/common'; +import { ABP } from '../models'; import { ReplaceableComponents } from '../models/replaceable-components'; -import { ConfigState } from '../states/config.state'; +import { RoutesService } from '../services/routes.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; +import { getRoutePath } from '../utils/route-utils'; import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { TreeNode } from '../utils/tree-utils'; @Component({ selector: 'abp-dynamic-layout', @@ -22,23 +23,37 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; export class DynamicLayoutComponent implements OnDestroy { layout: Type; - constructor(private router: Router, private route: ActivatedRoute, private store: Store) { - const { routes } = this.store.selectSnapshot(ConfigState.getAll); + constructor( + injector: Injector, + private route: ActivatedRoute, + private routes: RoutesService, + private store: Store, + ) { + const router = injector.get(Router); + const layouts = { + application: this.getComponent('Theme.ApplicationLayoutComponent'), + account: this.getComponent('Theme.AccountLayoutComponent'), + empty: this.getComponent('Theme.EmptyLayoutComponent'), + }; router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { - const segments = snq(() => router.parseUrl(event.url).root.children.primary.segments, [ - { path: router.url.replace('/', '') }, - ] as any); + let expectedLayout = (this.route.snapshot.data || {}).layout; + const path = getRoutePath(router); - const layouts = { - application: this.getComponent('Theme.ApplicationLayoutComponent'), - account: this.getComponent('Theme.AccountLayoutComponent'), - empty: this.getComponent('Theme.EmptyLayoutComponent'), - }; + if (!expectedLayout) { + let node = { parent: this.routes.search({ path }) } as TreeNode; + while (node.parent) { + node = node.parent; - const expectedLayout = - (this.route.snapshot.data || {}).layout || findLayout(segments, routes); + if (node.layout) { + expectedLayout = node.layout; + break; + } + } + } + + if (!expectedLayout) expectedLayout = eLayoutType.empty; this.layout = layouts[expectedLayout].component; } @@ -51,27 +66,3 @@ export class DynamicLayoutComponent implements OnDestroy { ngOnDestroy() {} } - -function findLayout(segments: UrlSegment[], routes: ABP.FullRoute[]): eLayoutType { - let layout = eLayoutType.empty; - - const route = routes - .reduce((acc, val) => (val.wrapper ? [...acc, ...val.children] : [...acc, val]), []) - .find(r => r.path === segments[0].path); - - if (route) { - if (route.layout) { - layout = route.layout; - } - - if (route.children && route.children.length && segments.length > 1) { - const child = route.children.find(c => c.path === segments[1].path); - - if (child && child.layout) { - layout = child.layout; - } - } - } - - return layout; -} diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index e1a678a19f..89a6a683b9 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -1,13 +1,13 @@ +import { HttpClient } from '@angular/common/http'; import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; import { NgxsModule, Store } from '@ngxs/store'; import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; -import { eLayoutType } from '../enums'; +import { eLayoutType } from '../enums/common'; import { ABP } from '../models'; -import { ConfigState, ReplaceableComponentsState } from '../states'; -import { ApplicationConfigurationService } from '../services'; -import { HttpClient } from '@angular/common/http'; +import { ApplicationConfigurationService, RoutesService } from '../services'; +import { ReplaceableComponentsState } from '../states'; @Component({ selector: 'abp-layout-application', @@ -48,28 +48,36 @@ class DummyComponent { constructor(public route: ActivatedRoute) {} } -const storeData = { - ConfigState: { - routes: [ - { - path: '', - wrapper: true, - children: [ - { - path: 'parentWithLayout', - layout: eLayoutType.application, - children: [ - { path: 'childWithoutLayout' }, - { path: 'childWithLayout', layout: eLayoutType.account }, - ], - }, - ], - }, - { path: 'withData', layout: eLayoutType.application }, - , - ] as ABP.FullRoute[], - environment: { application: {} }, +const routes: ABP.Route[] = [ + { + path: '', + name: 'Root', + }, + { + path: '/parentWithLayout', + name: 'ParentWithLayout', + parentName: 'Root', + layout: eLayoutType.application, + }, + { + path: '/parentWithLayout/childWithoutLayout', + name: 'ChildWithoutLayout', + parentName: 'ParentWithLayout', + }, + { + path: '/parentWithLayout/childWithLayout', + name: 'ChildWithLayout', + parentName: 'ParentWithLayout', + layout: eLayoutType.account, + }, + { + path: '/withData', + name: 'WithData', + layout: eLayoutType.application, }, +]; + +const storeData = { ReplaceableComponentsState: { replaceableComponents: [ { @@ -94,11 +102,7 @@ describe('DynamicLayoutComponent', () => { stubsEnabled: false, declarations: [DummyComponent, DynamicLayoutComponent], mocks: [ApplicationConfigurationService, HttpClient], - imports: [ - RouterModule, - DummyLayoutModule, - NgxsModule.forRoot([ConfigState, ReplaceableComponentsState]), - ], + imports: [RouterModule, DummyLayoutModule, NgxsModule.forRoot([ReplaceableComponentsState])], routes: [ { path: '', component: RouterOutletComponent }, { @@ -150,6 +154,8 @@ describe('DynamicLayoutComponent', () => { beforeEach(async () => { spectator = createComponent(); store = spectator.get(Store); + const routesService = spectator.get(RoutesService); + routesService.add(routes); store.reset(storeData); });