Browse Source

feat(core): get layouts from store in dynamic-layout.component

pull/3384/head
mehmet-erim 7 years ago
parent
commit
857ee6b9ef
  1. 49
      npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts
  2. 102
      npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts

49
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<Config.Requirements>;
@Select(ReplaceableComponentsState.getComponent('Theme.ApplicationLayoutComponent'))
applicationLayout$: Observable<ReplaceableComponents.ReplaceableComponent>;
@Select(ReplaceableComponentsState.getComponent('Theme.AccountLayoutComponent'))
accountLayout$: Observable<ReplaceableComponents.ReplaceableComponent>;
@Select(ReplaceableComponentsState.getComponent('Theme.EmptyLayoutComponent'))
emptyLayout$: Observable<ReplaceableComponents.ReplaceableComponent>;
layout: Type<any>;
layouts = {} as { [key in eLayoutType]: Type<any> };
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 {

102
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: '<router-outlet></router-outlet>',
})
class DummyApplicationLayoutComponent {
static type = eLayoutType.application;
}
class DummyApplicationLayoutComponent {}
@Component({
selector: 'abp-layout-account',
template: '<router-outlet></router-outlet>',
})
class DummyAccountLayoutComponent {
static type = eLayoutType.account;
}
class DummyAccountLayoutComponent {}
@Component({
selector: 'abp-layout-empty',
template: '<router-outlet></router-outlet>',
})
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<RouterOutletComponent>;
let store: SpyObject<Store>;
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();

Loading…
Cancel
Save