import { TemplatePortalDirective } from '@angular/cdk/portal'; import { ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, forwardRef, HostBinding, Input, OnChanges, OnInit, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, } from '@angular/core'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { IControlValueAccessor, mixinControlValueAccessor } from '../shared/behaviors'; import { PtDynamicElement, PtDynamicFormsService, PtDynamicType, PtFilterType } from './services/dynamic-forms.service'; import { Observable } from 'rxjs/internal/Observable'; export class PtDynamicElementBase { constructor(public _changeDetectorRef: ChangeDetectorRef) { } } /* tslint:disable-next-line */ export const _PtDynamicElementMixinBase = mixinControlValueAccessor(PtDynamicElementBase); /* tslint:disable-next-line */ @Directive({ selector: '[tdDynamicFormsError]ng-template' }) export class PtDynamicFormsErrorTemplateDirective extends TemplatePortalDirective { @Input() tdDynamicFormsError: string; // tslint:disable-next-line:no-any constructor(public templateRef: TemplateRef, viewContainerRef: ViewContainerRef) { super(templateRef, viewContainerRef); } } /* tslint:disable-next-line */ @Directive({ selector: '[tdDynamicSelectOption]ng-template' }) export class PtDynamicSelectionOptionTemplateDirective extends TemplatePortalDirective { @Input() tdDynamicSelectOption: string; // tslint:disable-next-line:no-any constructor(public templateRef: TemplateRef, viewContainerRef: ViewContainerRef) { super(templateRef, viewContainerRef); } } @Directive({ /* tslint:disable-next-line */ selector: '[tdDynamicContainer]', }) export class PtDynamicElementDirective { constructor(public viewContainer: ViewContainerRef) { } } @Component({ providers: [ PtDynamicFormsService, { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PtDynamicElementComponent), multi: true, }, ], selector: 'prutech-dynamic-element', template: '
', }) export class PtDynamicElementComponent extends _PtDynamicElementMixinBase implements IControlValueAccessor, OnInit, OnChanges { // tslint:disable-next-line:no-any private _instance: any; /** * Sets form control of the element. */ @Input() dynamicControl: FormControl; /** * Sets label to be displayed. */ @Input() label: string = ''; @Input() placeholder: string = ''; /** * Sets hint to be displayed. */ @Input() hint: string = ''; @Input() showLabel: boolean = false; @Input() showLabelAbove: boolean = false; @Input() lookupKey: string = ''; /** * Sets name to be displayed as attribute. */ @Input() name: string = ''; /** * Sets name to be displayed as attribute. */ @Input() value: string = ''; /** * Sets name to be displayed as attribute. */ @Input() filterType: PtFilterType; /** * Sets name to be displayed as attribute. */ @Input() readonly: boolean = undefined; /** * Sets name to be displayed as attribute. */ @Input() appearance: string = ''; /** * Sets name to be displayed as attribute. */ @Input() displayWith: Function; /** * Sets min validation checkup (if supported by element). */ @Input() dateFormat: string = undefined; /** * Sets type or element of element to be rendered. * Throws error if does not exist or no supported. */ // tslint:disable-next-line:no-any @Input() type: PtDynamicElement | PtDynamicType | Type = undefined; /** * Sets required validation checkup (if supported by element). */ @Input() required: boolean = undefined; /** * Sets required validation checkup (if supported by element). */ @Input() showClear: boolean = undefined; /** * Sets required validation checkup (if supported by element). */ @Input() filter: boolean = undefined; /** * Sets min validation checkup (if supported by element). */ @Input() min: number = undefined; /** * Sets max validation checkup (if supported by element). */ @Input() max: number = undefined; /** * Sets minLength validation checkup (if supported by element). */ @Input() minLength: number = undefined; /** * Sets maxLength validation checkup (if supported by element). */ @Input() maxLength: number = undefined; /** * Sets selections for array elements (if supported by element). */ // tslint:disable-next-line:no-any @Input() selections: Observable | any[] = undefined; /** * Sets loading for async requests (if supported by element). */ // tslint:disable-next-line:no-any @Input() loading: Observable = undefined; /** * Sets multiple property for array elements (if supported by element). */ @Input() multiple: boolean = undefined; /** * Sets select all option for multiselect controls for array elements (if supported by element). * required: multiple = true */ @Input() multiselectAll: boolean = undefined; /** * Sets hidden property (if supported by element). */ @Input() hidden: boolean = undefined; /** * Sets hidden property (if supported by element). */ @Input() disabled: boolean = undefined; /** * Sets error message template so it can be injected into dynamic components. */ // tslint:disable-next-line:no-any @Input() errorMessageTemplate: TemplateRef = undefined; // tslint:disable-next-line:no-any @Input() optionTemplate: TemplateRef = undefined; @ViewChild(PtDynamicElementDirective, { static: true }) childElement: PtDynamicElementDirective; constructor(private _componentFactoryResolver: ComponentFactoryResolver, private _dynamicFormsService: PtDynamicFormsService, _changeDetectorRef: ChangeDetectorRef) { super(_changeDetectorRef); } @HostBinding('attr.max') // tslint:disable-next-line:no-any get maxAttr(): any { return this.max; } @HostBinding('attr.min') // tslint:disable-next-line:no-any get minAttr(): any { return this.min; } ngOnInit(): void { // tslint:disable-next-line:no-any const component: any = // tslint:disable-next-line:no-any this.type instanceof Type ? this.type : this._dynamicFormsService.getDynamicElement(this.type, this.readonly); // tslint:disable-next-line:no-any const ref: ComponentRef = this._componentFactoryResolver .resolveComponentFactory(component) .create(this.childElement.viewContainer.injector); this.childElement.viewContainer.insert(ref.hostView); this._instance = ref.instance; this._instance.control = this.dynamicControl; this._instance.label = this.label; this._instance.placeholder = this.placeholder; this._instance.hint = this.hint; this._instance.name = this.name; this._instance.type = this.type; this._instance.value = this.value; this._instance.filter = this.filter; this._instance.filterType = this.filterType; this._instance.readonly = this.readonly; this._instance.required = this.required; this._instance.showClear = this.showClear; this._instance.dateFormat = this.dateFormat; this._instance.appearance = this.appearance; this._instance.min = this.min; this._instance.max = this.max; this._instance.minLength = this.minLength; this._instance.maxLength = this.maxLength; this._instance.selections = this.selections; this._instance.loading = this.loading; this._instance.multiple = this.multiple; this._instance.showLabel = this.showLabel; this._instance.showLabelAbove = this.showLabelAbove; this._instance.multiselectAll = this.multiselectAll; this._instance.hidden = this.hidden; this._instance.disabled = this.disabled; this._instance.lookupKey = this.lookupKey; this._instance.errorMessageTemplate = this.errorMessageTemplate; this._instance.optionTemplate = this.optionTemplate; this._instance.displayWith = this.displayWith; } /** * Reassign any inputs that have changed */ ngOnChanges(changes: SimpleChanges): void { if (this._instance) { for (const prop of Object.keys(changes)) { this._instance[prop] = changes[prop].currentValue; } } } }