From eedab03ad1634097bebf21542071933768b9e5e5 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 26 Nov 2019 17:05:34 +0300 Subject: [PATCH 01/10] feat(core): add template ref to permission directive --- .../lib/directives/permission.directive.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index 68a5ae6f753..1047d6ba30d 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -1,4 +1,14 @@ -import { Directive, ElementRef, Input, OnDestroy, OnInit, Optional, Renderer2 } from '@angular/core'; +import { + Directive, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef, + TemplateRef, + Optional, +} from '@angular/core'; import { Store } from '@ngxs/store'; import { ConfigState } from '../states'; import { takeUntilDestroy } from '../utils'; @@ -9,7 +19,13 @@ import { takeUntilDestroy } from '../utils'; export class PermissionDirective implements OnInit, OnDestroy { @Input('abpPermission') condition: string; - constructor(@Optional() private elRef: ElementRef, private renderer: Renderer2, private store: Store) {} + constructor( + private elRef: ElementRef, + private renderer: Renderer2, + private store: Store, + @Optional() private templateRef: TemplateRef, + private vcRef: ViewContainerRef, + ) {} ngOnInit() { if (this.condition) { @@ -17,7 +33,11 @@ export class PermissionDirective implements OnInit, OnDestroy { .select(ConfigState.getGrantedPolicy(this.condition)) .pipe(takeUntilDestroy(this)) .subscribe(isGranted => { - if (!isGranted) { + if (this.templateRef && isGranted) { + this.vcRef.createEmbeddedView(this.templateRef); + } else if (this.templateRef && !isGranted) { + this.vcRef.clear(); + } else if (!isGranted && !this.templateRef) { this.renderer.removeChild( (this.elRef.nativeElement as HTMLElement).parentElement, this.elRef.nativeElement, From a3d751f482d4ca4ab9b8a23896b789479376e820 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 26 Nov 2019 17:59:23 +0300 Subject: [PATCH 02/10] feat(core): support to find requiredPolicy from child route --- .../packages/core/src/lib/guards/permission.guard.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts index 97c6c3ebc8b..22d3e91414f 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; import { Store } from '@ngxs/store'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -13,8 +13,14 @@ import { ConfigState } from '../states'; export class PermissionGuard implements CanActivate { constructor(private store: Store) {} - canActivate({ data }: ActivatedRouteSnapshot): Observable { - const resource = snq(() => data.routes.requiredPolicy) || (data.requiredPolicy as string); + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + let resource = snq(() => route.data.routes.requiredPolicy) || (route.data.requiredPolicy as string); + if (!resource) { + resource = snq( + () => route.routeConfig.children.find(child => state.url.indexOf(child.path) > -1).data.requiredPolicy, + ); + } + return this.store.select(ConfigState.getGrantedPolicy(resource)).pipe( tap(access => { if (!access) { From 95563de2a414f2b96a52e63e85cf7d256dcccbaa Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:01:54 +0300 Subject: [PATCH 03/10] fix(core): add view clear before creating view add snq to permission guard --- .../packages/core/src/lib/directives/permission.directive.ts | 1 + npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index 1047d6ba30d..54657235c8d 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -34,6 +34,7 @@ export class PermissionDirective implements OnInit, OnDestroy { .pipe(takeUntilDestroy(this)) .subscribe(isGranted => { if (this.templateRef && isGranted) { + this.vcRef.clear(); this.vcRef.createEmbeddedView(this.templateRef); } else if (this.templateRef && !isGranted) { this.vcRef.clear(); diff --git a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts index 22d3e91414f..e23eeb40c22 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts @@ -14,7 +14,7 @@ export class PermissionGuard implements CanActivate { constructor(private store: Store) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - let resource = snq(() => route.data.routes.requiredPolicy) || (route.data.requiredPolicy as string); + let resource = snq(() => route.data.routes.requiredPolicy) || snq(() => route.data.requiredPolicy as string); if (!resource) { resource = snq( () => route.routeConfig.children.find(child => state.url.indexOf(child.path) > -1).data.requiredPolicy, From ec7f1f3f87fe86b2c4da15b704791a112b14c5e7 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:02:23 +0300 Subject: [PATCH 04/10] test(core): add nest tests to permission directive and guard --- .../lib/tests/permission.directive.spec.ts | 22 +++++++++++++++++++ .../src/lib/tests/permission.guard.spec.ts | 18 +++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts index c1d324620d2..9dff7425a03 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts @@ -43,4 +43,26 @@ describe('PermissionDirective', () => { expect(spy.mock.calls).toHaveLength(0); }); }); + + describe('structural', () => { + beforeEach(() => { + spectator = createDirective(`
Testing Permission Directive
`); + directive = spectator.directive; + }); + + it('should be created', () => { + expect(directive).toBeTruthy(); + }); + + it('should remove the element from DOM', () => { + expect(spectator.query('#test-element')).toBeFalsy(); + grantedPolicy$.next(true); + expect(spectator.query('#test-element')).toBeTruthy(); + grantedPolicy$.next(false); + expect(spectator.query('#test-element')).toBeFalsy(); + grantedPolicy$.next(true); + grantedPolicy$.next(true); + expect(spectator.queryAll('#test-element')).toHaveLength(1); + }); + }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts index b3580058377..a995a4c8229 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.guard.spec.ts @@ -23,7 +23,7 @@ describe('PermissionGuard', () => { it('should return true when the grantedPolicy is true', done => { store.select.andReturn(of(true)); const spy = jest.spyOn(store, 'dispatch'); - guard.canActivate({ data: { requiredPolicy: '' } } as any).subscribe(res => { + guard.canActivate({ data: { requiredPolicy: 'test' } } as any, null).subscribe(res => { expect(res).toBe(true); expect(spy.mock.calls).toHaveLength(0); done(); @@ -33,11 +33,25 @@ describe('PermissionGuard', () => { it('should return false and dispatch RestOccurError when the grantedPolicy is false', done => { store.select.andReturn(of(false)); const spy = jest.spyOn(store, 'dispatch'); - guard.canActivate({ data: { requiredPolicy: '' } } as any).subscribe(res => { + guard.canActivate({ data: { requiredPolicy: 'test' } } as any, null).subscribe(res => { expect(res).toBe(false); expect(spy.mock.calls[0][0] instanceof RestOccurError).toBeTruthy(); expect((spy.mock.calls[0][0] as RestOccurError).payload).toEqual({ status: 403 }); done(); }); }); + + it('should find the requiredPolicy from child route', done => { + store.select.andReturn(of(false)); + const spy = jest.spyOn(store, 'select'); + guard + .canActivate( + { data: {}, routeConfig: { children: [{ path: 'test', data: { requiredPolicy: 'TestPolicy' } }] } } as any, + { url: 'test' } as any, + ) + .subscribe(() => { + expect(spy.mock.calls[0][0]({ auth: { grantedPolicies: { TestPolicy: true } } })).toBe(true); + done(); + }); + }); }); From 9be940181e2d68bd9bb7af44487b81996f4431ac Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:02:51 +0300 Subject: [PATCH 05/10] feat(core): add toLocalISOString function to Date prototype --- npm/ng-packs/packages/core/src/lib/core.module.ts | 1 + .../packages/core/src/lib/utils/date-extensions.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/utils/date-extensions.ts 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 67350b88ce0..b3c687672d4 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -28,6 +28,7 @@ import { ConfigState } from './states/config.state'; import { ProfileState } from './states/profile.state'; import { SessionState } from './states/session.state'; import { getInitialData, localeInitializer } from './utils/initial-utils'; +import './utils/date-extensions'; @NgModule({ imports: [ diff --git a/npm/ng-packs/packages/core/src/lib/utils/date-extensions.ts b/npm/ng-packs/packages/core/src/lib/utils/date-extensions.ts new file mode 100644 index 00000000000..3522b000c8d --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/date-extensions.ts @@ -0,0 +1,13 @@ +export {}; + +declare global { + interface Date { + toLocalISOString(): string; + } +} + +Date.prototype.toLocalISOString = function(this: Date): string { + const timezoneOffset = this.getTimezoneOffset(); + + return new Date(this.getTime() - timezoneOffset * 60000).toISOString(); +}; From f21917ab2bb5f5dce51dde1a5e9fbbef36bc9f90 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:03:20 +0300 Subject: [PATCH 06/10] fix(tenant-management): add type error control --- .../src/lib/components/tenants/tenants.component.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts index 18387ea784c..49fadbe674b 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts @@ -77,9 +77,13 @@ export class TenantsComponent implements OnInit { get isDisabledSaveButton(): boolean { if (!this.selectedModalContent) return false; - if (this.selectedModalContent.type === 'saveConnStr' && this.defaultConnectionStringForm.invalid) { + if ( + this.selectedModalContent.type === 'saveConnStr' && + this.defaultConnectionStringForm && + this.defaultConnectionStringForm.invalid + ) { return true; - } else if (this.selectedModalContent.type === 'saveTenant' && this.tenantForm.invalid) { + } else if (this.selectedModalContent.type === 'saveTenant' && this.tenantForm && this.tenantForm.invalid) { return true; } else { return false; From d17d6b1c16dca21c682b116d2d3847f10e4856bd Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:07:30 +0300 Subject: [PATCH 07/10] refactor(theme-shared): rename error.component BREAKING CHANGE: renamed error.component to http-error-wrapper.component --- .../http-error-wrapper.component.html} | 2 +- .../http-error-wrapper.component.scss} | 1 - .../http-error-wrapper.component.ts} | 19 +++++++++++++------ .../src/lib/handlers/error.handler.ts | 8 ++++---- .../src/lib/tests/error.component.spec.ts | 8 ++++---- .../src/lib/tests/error.handler.spec.ts | 14 ++++++++++---- .../src/lib/theme-shared.module.ts | 6 +++--- 7 files changed, 35 insertions(+), 23 deletions(-) rename npm/ng-packs/packages/theme-shared/src/lib/components/{error/error.component.html => http-error-wrapper/http-error-wrapper.component.html} (88%) rename npm/ng-packs/packages/theme-shared/src/lib/components/{error/error.component.scss => http-error-wrapper/http-error-wrapper.component.scss} (87%) rename npm/ng-packs/packages/theme-shared/src/lib/components/{error/error.component.ts => http-error-wrapper/http-error-wrapper.component.ts} (81%) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.html similarity index 88% rename from npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html rename to npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.html index 8801240bbf7..95f259225c7 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.scss similarity index 87% rename from npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss rename to npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.scss index a98b5a3ca69..b9f7690a5b4 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.scss @@ -1,7 +1,6 @@ .error { position: fixed; top: 0; - background-color: #fff; width: 100vw; height: 100vh; z-index: 999999; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts similarity index 81% rename from npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts rename to npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts index 70ddce726ee..35ef50bf2a0 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts @@ -1,25 +1,26 @@ import { Config, takeUntilDestroy } from '@abp/ng.core'; import { AfterViewInit, + ApplicationRef, Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, + Injector, OnDestroy, + OnInit, Type, ViewChild, - ApplicationRef, - Injector, } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { debounceTime, filter } from 'rxjs/operators'; @Component({ - selector: 'abp-error', - templateUrl: './error.component.html', - styleUrls: ['error.component.scss'], + selector: 'abp-http-error-wrapper', + templateUrl: './http-error-wrapper.component.html', + styleUrls: ['http-error-wrapper.component.scss'], }) -export class ErrorComponent implements AfterViewInit, OnDestroy { +export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnInit { appRef: ApplicationRef; cfRes: ComponentFactoryResolver; @@ -38,6 +39,8 @@ export class ErrorComponent implements AfterViewInit, OnDestroy { hideCloseIcon = false; + backgroundColor: string; + @ViewChild('container', { static: false }) containerRef: ElementRef; @@ -45,6 +48,10 @@ export class ErrorComponent implements AfterViewInit, OnDestroy { return this.status ? `[${this.status}]` : ''; } + ngOnInit() { + this.backgroundColor = window.getComputedStyle(document.body).getPropertyValue('background-color'); + } + ngAfterViewInit() { if (this.customComponent) { const customComponentRef = this.cfRes.resolveComponentFactory(this.customComponent).create(this.injector); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index ec0d3ac8082..e4663c38aa3 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -15,7 +15,7 @@ import { Navigate, RouterError, RouterState, RouterDataResolved } from '@ngxs/ro import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { Observable, Subject } from 'rxjs'; import snq from 'snq'; -import { ErrorComponent } from '../components/error/error.component'; +import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component'; import { HttpErrorConfig, ErrorScreenErrorCodes } from '../models/common'; import { Toaster } from '../models/toaster'; import { ConfirmationService } from '../services/confirmation.service'; @@ -45,7 +45,7 @@ export const DEFAULT_ERROR_MESSAGES = { @Injectable({ providedIn: 'root' }) export class ErrorHandler { - componentRef: ComponentRef; + componentRef: ComponentRef; constructor( private actions: Actions, @@ -196,11 +196,11 @@ export class ErrorHandler { ); } - createErrorComponent(instance: Partial) { + createErrorComponent(instance: Partial) { const renderer = this.rendererFactory.createRenderer(null, null); const host = renderer.selectRootElement(document.body, true); - this.componentRef = this.cfRes.resolveComponentFactory(ErrorComponent).create(this.injector); + this.componentRef = this.cfRes.resolveComponentFactory(HttpErrorWrapperComponent).create(this.injector); for (const key in this.componentRef.instance) { if (this.componentRef.instance.hasOwnProperty(key)) { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts index 05e303e4c98..1d1f256c688 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts @@ -1,14 +1,14 @@ import { SpectatorHost, createHostFactory } from '@ngneat/spectator/jest'; -import { ErrorComponent } from '../components/error/error.component'; +import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component'; import { LocalizationPipe } from '@abp/ng.core'; import { Store } from '@ngxs/store'; import { Renderer2, ElementRef } from '@angular/core'; import { Subject } from 'rxjs'; describe('ErrorComponent', () => { - let spectator: SpectatorHost; + let spectator: SpectatorHost; const createHost = createHostFactory({ - component: ErrorComponent, + component: HttpErrorWrapperComponent, declarations: [LocalizationPipe], mocks: [Store], providers: [ @@ -18,7 +18,7 @@ describe('ErrorComponent', () => { }); beforeEach(() => { - spectator = createHost(''); + spectator = createHost(''); spectator.component.destroy$ = new Subject(); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts index d3265691de1..88ae00a874a 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts @@ -22,14 +22,17 @@ describe('ErrorHandler', () => { component: DummyComponent, imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot([])], stubsEnabled: false, - routes: [{ path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }], + routes: [ + { path: '', component: DummyComponent }, + { path: 'account/login', component: RouterOutletComponent }, + ], }); beforeEach(() => { spectator = createComponent(); store = spectator.get(Store); - const abpError = document.querySelector('abp-error'); + const abpError = document.querySelector('abp-http-error-wrapper'); if (abpError) document.body.removeChild(abpError); }); @@ -133,14 +136,17 @@ describe('ErrorHandler with custom error component', () => { ErrorModule, ], stubsEnabled: false, - routes: [{ path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }], + routes: [ + { path: '', component: DummyComponent }, + { path: 'account/login', component: RouterOutletComponent }, + ], }); beforeEach(() => { spectator = createComponent(); store = spectator.get(Store); - const abpError = document.querySelector('abp-error'); + const abpError = document.querySelector('abp-http-error-wrapper'); if (abpError) document.body.removeChild(abpError); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index f9cf4097cd2..7464df328aa 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -8,7 +8,7 @@ import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.componen import { ButtonComponent } from './components/button/button.component'; import { ChartComponent } from './components/chart/chart.component'; import { ConfirmationComponent } from './components/confirmation/confirmation.component'; -import { ErrorComponent } from './components/error/error.component'; +import { HttpErrorWrapperComponent } from './components/http-error-wrapper/http-error-wrapper.component'; import { LoaderBarComponent } from './components/loader-bar/loader-bar.component'; import { ModalComponent } from './components/modal/modal.component'; import { SortOrderIconComponent } from './components/sort-order-icon/sort-order-icon.component'; @@ -42,7 +42,7 @@ export function appendScript(injector: Injector) { ButtonComponent, ChartComponent, ConfirmationComponent, - ErrorComponent, + HttpErrorWrapperComponent, LoaderBarComponent, ModalComponent, TableEmptyMessageComponent, @@ -63,7 +63,7 @@ export function appendScript(injector: Injector) { TableSortDirective, ], providers: [DatePipe], - entryComponents: [ErrorComponent], + entryComponents: [HttpErrorWrapperComponent], }) export class ThemeSharedModule { constructor(private errorHandler: ErrorHandler) {} From 40641a63b40c8928591c7fd342c80097cfe5fe22 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:26:20 +0300 Subject: [PATCH 08/10] refactor(theme-shared): add snq for type error --- .../http-error-wrapper/http-error-wrapper.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts index 35ef50bf2a0..a102672ad64 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts @@ -14,6 +14,7 @@ import { } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { debounceTime, filter } from 'rxjs/operators'; +import snq from 'snq'; @Component({ selector: 'abp-http-error-wrapper', @@ -49,7 +50,8 @@ export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnIn } ngOnInit() { - this.backgroundColor = window.getComputedStyle(document.body).getPropertyValue('background-color'); + this.backgroundColor = + snq(() => window.getComputedStyle(document.body).getPropertyValue('background-color')) || '#fff'; } ngAfterViewInit() { From b12b607fc8b9c120c33446c342a0d494b918b704 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 09:57:17 +0300 Subject: [PATCH 09/10] refactor(core): add contidion change detection to permission directive --- .../lib/directives/permission.directive.ts | 52 ++++++++++++------- .../lib/tests/permission.directive.spec.ts | 21 +++++++- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index 54657235c8d..870e4ec144d 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -8,17 +8,22 @@ import { ViewContainerRef, TemplateRef, Optional, + SimpleChanges, + OnChanges, } from '@angular/core'; import { Store } from '@ngxs/store'; import { ConfigState } from '../states'; import { takeUntilDestroy } from '../utils'; +import { Subscription } from 'rxjs'; @Directive({ selector: '[abpPermission]', }) -export class PermissionDirective implements OnInit, OnDestroy { +export class PermissionDirective implements OnInit, OnDestroy, OnChanges { @Input('abpPermission') condition: string; + subscription: Subscription; + constructor( private elRef: ElementRef, private renderer: Renderer2, @@ -27,26 +32,37 @@ export class PermissionDirective implements OnInit, OnDestroy { private vcRef: ViewContainerRef, ) {} + private check() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + + this.subscription = this.store + .select(ConfigState.getGrantedPolicy(this.condition)) + .pipe(takeUntilDestroy(this)) + .subscribe(isGranted => { + if (this.templateRef && isGranted) { + this.vcRef.clear(); + this.vcRef.createEmbeddedView(this.templateRef); + } else if (this.templateRef && !isGranted) { + this.vcRef.clear(); + } else if (!isGranted && !this.templateRef) { + this.renderer.removeChild((this.elRef.nativeElement as HTMLElement).parentElement, this.elRef.nativeElement); + } + }); + } + ngOnInit() { - if (this.condition) { - this.store - .select(ConfigState.getGrantedPolicy(this.condition)) - .pipe(takeUntilDestroy(this)) - .subscribe(isGranted => { - if (this.templateRef && isGranted) { - this.vcRef.clear(); - this.vcRef.createEmbeddedView(this.templateRef); - } else if (this.templateRef && !isGranted) { - this.vcRef.clear(); - } else if (!isGranted && !this.templateRef) { - this.renderer.removeChild( - (this.elRef.nativeElement as HTMLElement).parentElement, - this.elRef.nativeElement, - ); - } - }); + if (this.templateRef && !this.condition) { + this.vcRef.createEmbeddedView(this.templateRef); } } ngOnDestroy(): void {} + + ngOnChanges({ condition }: SimpleChanges) { + if ((condition || { currentValue: null }).currentValue) { + this.check(); + } + } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts index 9dff7425a03..6d9dd3964f7 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts @@ -46,7 +46,10 @@ describe('PermissionDirective', () => { describe('structural', () => { beforeEach(() => { - spectator = createDirective(`
Testing Permission Directive
`); + spectator = createDirective( + '
Testing Permission Directive
', + { hostProps: { condition: '' } }, + ); directive = spectator.directive; }); @@ -55,7 +58,10 @@ describe('PermissionDirective', () => { }); it('should remove the element from DOM', () => { - expect(spectator.query('#test-element')).toBeFalsy(); + expect(spectator.query('#test-element')).toBeTruthy(); + expect(spectator.directive.subscription).toBeUndefined(); + spectator.setHostInput({ condition: 'test' }); + expect(spectator.directive.subscription).toBeTruthy(); grantedPolicy$.next(true); expect(spectator.query('#test-element')).toBeTruthy(); grantedPolicy$.next(false); @@ -64,5 +70,16 @@ describe('PermissionDirective', () => { grantedPolicy$.next(true); expect(spectator.queryAll('#test-element')).toHaveLength(1); }); + + describe('#subscription', () => { + it('should call the unsubscribe', () => { + const spy = jest.fn(() => {}); + spectator.setHostInput({ condition: 'test' }); + spectator.directive.subscription.unsubscribe = spy; + spectator.setHostInput({ condition: 'test2' }); + + expect(spy).toHaveBeenCalled(); + }); + }); }); }); From 20c5921072cf304930da4aa1c6ca8ed89e1f75d4 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 10:17:37 +0300 Subject: [PATCH 10/10] refactor: change abpPermission using type --- .../src/lib/components/roles/roles.component.html | 8 ++++---- .../src/lib/components/users/users.component.html | 8 ++++---- .../src/lib/components/tenants/tenants.component.html | 10 +++++----- .../application-layout.component.html | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html index fa0ee93d1a4..59fa3f4199a 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html +++ b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html @@ -6,7 +6,7 @@
{{ 'AbpIdentity::Roles' | abpLocalization }}
-
-