123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- import {
- Directive, ComponentFactoryResolver, ComponentRef, ViewContainerRef,
- Input, Output, EventEmitter, OnInit, OnDestroy,
- Optional, Self, SkipSelf, Inject
- } from '@angular/core';
- import {
- Form, FormControl, AbstractControl,
- NgControl, ControlContainer, ControlValueAccessor,
- NG_VALIDATORS, Validator, Validators, ValidatorFn, AsyncValidatorFn
- } from '@angular/forms';
- import * as formFieldComponents from './../components';
- interface FFC {
- control: FormControl; // Remember, this can be an individual FormControl or a FormGroup
- meta: StringMap;
- propagateChange?: Function;
- call?: EventEmitter<string>
- }
- type FFCCustom = FFC & ControlValueAccessor;
- // Generate component name given type
- const componentType = (type: string): string => type[0].toUpperCase() + type.slice(1) + 'Component';
- @Directive({
- // tslint:disable-next-line:directive-selector
- selector: '[dynafield]'
- })
- export class DynafieldDirective extends NgControl implements OnInit, OnDestroy {
- @Input()
- meta: StringMap;
- @Input()
- set control(fc: FormControl) {
- this._control = this._control || fc;
- }
- get control(): FormControl | null {
- return this._control;
- }
- // tslint:disable-next-line:no-output-rename
- @Output('ngModelChange')
- update = new EventEmitter();
- @Output()
- call: EventEmitter<string> = new EventEmitter<string>();
- component: ComponentRef<FFC|FFCCustom>;
- _control;
- constructor(
- private resolver: ComponentFactoryResolver,
- private container: ViewContainerRef,
- @SkipSelf() private cc: ControlContainer,
- @Optional() @Self() @Inject(NG_VALIDATORS) private validators: Array<Validator | ValidatorFn>
- ) {
- super();
- }
- ngOnInit() {
- const type = componentType(this.meta.type);
- if (!formFieldComponents[type]) {
- const validComponentTypes = Object.keys(formFieldComponents).join(', ');
- throw new Error(
- `Dynaform Dynafield Error: Invalid field type: ${type}.
- Supported types: ${validComponentTypes}`
- );
- }
- try {
- let { control, meta } = this;
- const { name, class: cssClass, id: cssId, isDisabled } = meta;
- // Create the component
- const component = this.resolver.resolveComponentFactory<FFC>(formFieldComponents[type]);
- this.component = this.container.createComponent(component);
- const instance = this.component.instance;
- const el = this.component.location.nativeElement;
- el.classList.add(type.toLowerCase().replace('component', ''));
- // Support the recursive insertion of Dynaform components
- if (type === 'DynaformComponent') {
- if (meta.template) {
- (<any>instance).template = meta.template;
- }
- meta = meta.meta;
- }
- // Check whether it's disabled, then set its FormControl and metadata
- if (isDisabled) {
- this.control.reset({ value: this.control.value, disabled: true });
- }
- instance.control = control;
- instance.meta = meta;
- // Listen for 'call' Output events and send them onwards
- if (instance.call) {
- instance.call.subscribe((fnId: string) => this.call.emit(fnId));
- }
- // Add id and classes (as specified)
- if (cssId) {
- el.id = cssId;
- }
- if (cssClass) {
- const classesToAdd = Array.isArray(cssClass) ? cssClass : [cssClass];
- el.classList.add(...classesToAdd);
- }
- // Connect custom components
- if (instance.propagateChange) {
- // We're dealing with a custom form control which implements the ControlValueAccessor interface,
- // so we need to wire it up!
- this.name = name;
- this.valueAccessor = <FFCCustom>this.component.instance;
- // Attach sync validators
- /*
- const ngValidators = this.component.injector.get(NG_VALIDATORS, null);
- if (ngValidators && ngValidators.some(x => x as any === this.component.instance)) {
- console.log('HERE');
- this.validators = [...(this.validators || []), ...(ngValidators as Array<Validator|ValidatorFn>)];
- }
- */
- this._control = this.formGroupDirective.addControl(this);
- }
- } catch (e) {
- console.error('ERROR INSTANTIATING DYNAFORM CHILD COMPONENT', type);
- console.log(e);
- }
- }
- ngOnDestroy(): void {
- if (this.formGroupDirective) {
- this.formGroupDirective.removeControl(this);
- }
- if (this.component) {
- this.component.destroy();
- }
- }
- get path(): string[] {
- return [...this.cc.path, this.name];
- }
- get formGroupDirective(): Form | null {
- return this.cc ? this.cc.formDirective : null;
- }
- get validator(): ValidatorFn | null {
- return this.validators !== null ? Validators.compose(this.validators.map(this.normalizeValidator)) : null;
- }
- get asyncValidator(): AsyncValidatorFn { return null; }
- // Override the method in NgControl
- viewToModelUpdate(newValue: any): void {
- this.update.emit(newValue);
- }
- normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
- if ((<Validator>validator).validate) {
- return (c: AbstractControl) => (<Validator>validator).validate(c);
- } else {
- return <ValidatorFn>validator;
- }
- }
-
- }
|