From 57403b86d163b0a54ed01e3fff99cfcda05da95f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 25 Dec 2019 18:37:06 +0300 Subject: [PATCH] feat(core): create replaceable-template directive #2404 --- .../packages/core/src/lib/core.module.ts | 16 ++- .../packages/core/src/lib/directives/index.ts | 2 + .../replaceable-template.directive.ts | 118 ++++++++++++++++++ 3 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.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 34b4825488..8a7f9bac57 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -31,6 +31,8 @@ import { getInitialData, localeInitializer } from './utils/initial-utils'; import './utils/date-extensions'; import { ReplaceableRouteContainerComponent } from './components/replaceable-route-container.component'; import { ReplaceableComponentsState } from './states/replaceable-components.state'; +import { InitDirective } from './directives/init.directive'; +import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; @NgModule({ imports: [ @@ -46,6 +48,7 @@ import { ReplaceableComponentsState } from './states/replaceable-components.stat ], declarations: [ ReplaceableRouteContainerComponent, + ReplaceableTemplateContainerComponent, RouterOutletComponent, DynamicLayoutComponent, AutofocusDirective, @@ -54,6 +57,7 @@ import { ReplaceableComponentsState } from './states/replaceable-components.stat FormSubmitDirective, LocalizationPipe, SortPipe, + InitDirective, PermissionDirective, VisibilityDirective, InputEventDebounceDirective, @@ -68,19 +72,21 @@ import { ReplaceableComponentsState } from './states/replaceable-components.stat RouterModule, RouterOutletComponent, DynamicLayoutComponent, + AbstractNgModelComponent, + ReplaceableRouteContainerComponent, AutofocusDirective, EllipsisDirective, ForDirective, FormSubmitDirective, - LocalizationPipe, - SortPipe, + InitDirective, PermissionDirective, VisibilityDirective, InputEventDebounceDirective, - LocalizationPipe, + ReplaceableTemplateDirective, StopPropagationDirective, - AbstractNgModelComponent, - ReplaceableRouteContainerComponent, + LocalizationPipe, + SortPipe, + LocalizationPipe, ], providers: [LocalizationPipe], entryComponents: [ diff --git a/npm/ng-packs/packages/core/src/lib/directives/index.ts b/npm/ng-packs/packages/core/src/lib/directives/index.ts index 7edf1b5f76..f191cfcef5 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/index.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/index.ts @@ -2,5 +2,7 @@ export * from './autofocus.directive'; export * from './ellipsis.directive'; export * from './for.directive'; export * from './form-submit.directive'; +export * from './init.directive'; export * from './permission.directive'; +export * from './replaceable-template.directive'; export * from './visibility.directive'; diff --git a/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts new file mode 100644 index 0000000000..7af076e6b4 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts @@ -0,0 +1,118 @@ +import { + ComponentFactoryResolver, + Directive, + Injector, + Input, + OnDestroy, + OnInit, + TemplateRef, + Type, + ViewContainerRef, + SimpleChanges, +} from '@angular/core'; +import { Store } from '@ngxs/store'; +import compare from 'just-compare'; +import { filter } from 'rxjs/operators'; +import { ReplaceableComponents } from '../models/replaceable-components'; +import { ReplaceableComponentsState } from '../states/replaceable-components.state'; +import { takeUntilDestroy } from '../utils/rxjs-utils'; + +@Directive({ selector: '[abpReplaceableTemplate]' }) +export class ReplaceableTemplateDirective implements OnInit, OnDestroy { + private context = {}; + + @Input('abpReplaceableTemplate') + data: { inputs: any; outputs: any; componentKey: string }; + + providedData = { inputs: {}, outputs: {} } as { inputs: any; outputs: any; componentKey: string }; + + externalComponent: Type = null; // externalComponent must equal to null + + defaultComponentRef: any; + + constructor( + private injector: Injector, + private templateRef: TemplateRef, + private cfRes: ComponentFactoryResolver, + private vcRef: ViewContainerRef, + private store: Store, + ) { + this.context = { + initTemplate: ref => { + this.defaultComponentRef = ref; + this.setDefaultComponentInputs(); + setTimeout(() => { + ref.providerKey = 'admin'; + ref.visible = true; + }, 5000); + }, + }; + } + + ngOnInit() { + this.store + .select(ReplaceableComponentsState.getComponent(this.data.componentKey)) + .pipe( + filter( + (res = {} as ReplaceableComponents.ReplaceableComponent) => + !compare(res.component, this.externalComponent), + ), + takeUntilDestroy(this), + ) + .subscribe((res = {} as ReplaceableComponents.ReplaceableComponent) => { + this.vcRef.clear(); + this.externalComponent = res.component; + + if (res.component) { + this.setProvidedData(); + const customInjector = Injector.create({ + providers: [{ provide: 'REPLACEABLE_DATA', useValue: this.providedData }], + parent: this.injector, + }); + this.vcRef.createComponent( + this.cfRes.resolveComponentFactory(res.component), + 0, + customInjector, + ); + } else { + this.vcRef.createEmbeddedView(this.templateRef, this.context); + } + }); + } + + ngOnChanges(changes: SimpleChanges) { + console.log(changes); + } + + ngOnDestroy() {} + + setDefaultComponentInputs() { + if (!this.defaultComponentRef) return; + } + + setProvidedData() { + Object.defineProperties(this.providedData.inputs, { + ...Object.keys(this.data.inputs).reduce( + (acc, key) => ({ + ...acc, + [key]: { + enumerable: true, + configurable: true, + get: () => this.data.inputs[key].value, + ...(this.data.inputs[key].twoWay && { + set: newValue => { + this.data.inputs[key].value = newValue; + this.data.outputs[`${key}Change`](newValue); + }, + }), + }, + }), + {}, + ), + }); + + this.providedData.outputs = this.data.outputs; + + console.warn(this.providedData); + } +}