From 857ee6b9ef82376c42229c613764feaee7896a95 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 10:22:45 +0300 Subject: [PATCH] feat(core): get layouts from store in dynamic-layout.component --- .../components/dynamic-layout.component.ts | 49 +++++---- .../tests/dynamic-layout.component.spec.ts | 102 +++++++++++------- 2 files changed, 93 insertions(+), 58 deletions(-) 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 7fc1826efe..fc3926a1ea 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,15 @@ import { Component, Input, OnDestroy, Type, Injector } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router'; import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; +import { Observable, combineLatest } from 'rxjs'; import snq from 'snq'; import { eLayoutType } from '../enums/common'; import { Config } from '../models/config'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { ReplaceableComponentsState } from '../states/replaceable-components.state'; +import { ReplaceableComponents } from '../models/replaceable-components'; @Component({ selector: 'abp-dynamic-layout', @@ -20,24 +22,24 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; `, }) export class DynamicLayoutComponent implements OnDestroy { - @Select(ConfigState.getOne('requirements')) requirements$: Observable; + @Select(ReplaceableComponentsState.getComponent('Theme.ApplicationLayoutComponent')) + applicationLayout$: Observable; + + @Select(ReplaceableComponentsState.getComponent('Theme.AccountLayoutComponent')) + accountLayout$: Observable; + + @Select(ReplaceableComponentsState.getComponent('Theme.EmptyLayoutComponent')) + emptyLayout$: Observable; layout: Type; + layouts = {} as { [key in eLayoutType]: Type }; + + expectedLayout: eLayoutType = eLayoutType.empty; + constructor(private router: Router, private route: ActivatedRoute, private store: Store) { - const { - requirements: { layouts }, - routes, - } = this.store.selectSnapshot(ConfigState.getAll); - - if ((this.route.snapshot.data || {}).layout) { - this.layout = layouts - .filter(l => !!l) - .find( - (l: any) => - snq(() => l.type.toLowerCase().indexOf(this.route.snapshot.data.layout), -1) > -1, - ); - } + this.listenToLayouts(); + const { routes } = this.store.selectSnapshot(ConfigState.getAll); router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { @@ -45,16 +47,25 @@ export class DynamicLayoutComponent implements OnDestroy { { path: router.url.replace('/', '') }, ] as any); - const layout = (this.route.snapshot.data || {}).layout || findLayout(segments, routes); + this.expectedLayout = + (this.route.snapshot.data || {}).layout || findLayout(segments, routes); - this.layout = layouts - .filter(l => !!l) - .find((l: any) => snq(() => l.type.toLowerCase().indexOf(layout), -1) > -1); + this.layout = this.layouts[this.expectedLayout]; } }); } ngOnDestroy() {} + + listenToLayouts() { + combineLatest(this.applicationLayout$, this.accountLayout$, this.emptyLayout$) + .pipe(takeUntilDestroy(this)) + .subscribe(([application, account, empty]) => { + this.layouts.application = application.component; + this.layouts.account = account.component; + this.layouts.empty = empty.component; + }); + } } function findLayout(segments: UrlSegment[], routes: ABP.FullRoute[]): eLayoutType { 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 54a602b84a..0a123b6a4f 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,36 +1,36 @@ import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; -import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator/jest'; -import { Store } from '@ngxs/store'; +import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; +import { NgxsModule, Store } from '@ngxs/store'; +import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; import { eLayoutType } from '../enums'; import { ABP } from '../models'; -import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; +import { ConfigState, ReplaceableComponentsState } from '../states'; +import { ApplicationConfigurationService } from '../services'; @Component({ selector: 'abp-layout-application', template: '', }) -class DummyApplicationLayoutComponent { - static type = eLayoutType.application; -} +class DummyApplicationLayoutComponent {} @Component({ selector: 'abp-layout-account', template: '', }) -class DummyAccountLayoutComponent { - static type = eLayoutType.account; -} +class DummyAccountLayoutComponent {} @Component({ selector: 'abp-layout-empty', template: '', }) -class DummyEmptyLayoutComponent { - static type = eLayoutType.empty; -} +class DummyEmptyLayoutComponent {} -const LAYOUTS = [DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent]; +const LAYOUTS = [ + DummyApplicationLayoutComponent, + DummyAccountLayoutComponent, + DummyEmptyLayoutComponent, +]; @NgModule({ imports: [RouterModule], @@ -47,13 +47,57 @@ 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: {} }, + }, + ReplaceableComponentsState: { + replaceableComponents: [ + { + key: 'Theme.ApplicationLayoutComponent', + component: DummyApplicationLayoutComponent, + }, + { + key: 'Theme.AccountLayoutComponent', + component: DummyAccountLayoutComponent, + }, + { + key: 'Theme.EmptyLayoutComponent', + component: DummyEmptyLayoutComponent, + }, + ], + }, +}; + describe('DynamicLayoutComponent', () => { const createComponent = createRoutingFactory({ component: RouterOutletComponent, stubsEnabled: false, - mocks: [Store], declarations: [DummyComponent, DynamicLayoutComponent], - imports: [RouterModule, DummyLayoutModule], + mocks: [ApplicationConfigurationService], + imports: [ + RouterModule, + DummyLayoutModule, + NgxsModule.forRoot([ConfigState, ReplaceableComponentsState]), + ], routes: [ { path: '', component: RouterOutletComponent }, { @@ -100,33 +144,13 @@ describe('DynamicLayoutComponent', () => { }); let spectator: SpectatorRouting; - let store: SpyObject; - const mockStoreData = { - requirements: { layouts: LAYOUTS }, - 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: {} }, - }; - let storeSpy: jest.SpyInstance; + let store: Store; beforeEach(async () => { spectator = createComponent(); store = spectator.get(Store); - storeSpy = jest.spyOn(store, 'selectSnapshot'); - storeSpy.mockReturnValue(mockStoreData); + + store.reset(storeData); }); it('should handle application layout from parent abp route and display it', async () => { @@ -159,7 +183,7 @@ describe('DynamicLayoutComponent', () => { }); it('should not display any layout when layouts are empty', async () => { - storeSpy.mockReturnValue({ ...mockStoreData, requirements: { layouts: [] } }); + store.reset({ ...storeData, ReplaceableComponentsState: {} }); spectator.detectChanges();