Browse Source

custom form field support added

pull/24547/head
erdemcaygor 1 year ago
parent
commit
3f2f25eae9
  1. 45
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field-host.component.ts
  2. 10
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field.component.html
  3. 20
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field.component.ts
  4. 4
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form.models.ts

45
npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field-host.component.ts

@ -2,8 +2,6 @@ import {
Component, Component,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
OnChanges,
SimpleChanges,
ChangeDetectionStrategy, ChangeDetectionStrategy,
forwardRef, forwardRef,
Type, Type,
@ -34,26 +32,28 @@ type acceptsFormControl = { formControl?: FormControl };
multi: true multi: true
}] }]
}) })
export class DynamicFieldHostComponent implements ControlValueAccessor, OnChanges { export class DynamicFieldHostComponent implements ControlValueAccessor {
component = input<Type<any>>(); component = input<Type<ControlValueAccessor>>();
inputs = input<Record<string, any>>({}); inputs = input<Record<string, any>>({});
@ViewChild('vcRef', { read: ViewContainerRef, static: true }) viewContainerRef!: ViewContainerRef; @ViewChild('vcRef', { read: ViewContainerRef, static: true }) viewContainerRef!: ViewContainerRef;
private componentRef?: any; private componentRef?: any;
private lastValue: any; private value: any;
private disabled = false; private disabled = false;
// if child has not implemented ControlValueAccessor. Create form control // if child has not implemented ControlValueAccessor. Create form control
private innerControl = new FormControl<any>(null); private innerControl = new FormControl<any>(null);
readonly destroyRef = inject(DestroyRef); readonly destroyRef = inject(DestroyRef);
ngOnChanges(changes: SimpleChanges): void { constructor() {
if (changes['component']) { effect(() => {
this.createChild(); if (this.component()) {
} else if (this.componentRef && changes['inputs']) { this.createChild();
this.applyInputs(); } else if (this.componentRef && this.inputs()) {
} this.applyInputs();
}
});
} }
private createChild() { private createChild() {
@ -72,25 +72,20 @@ export class DynamicFieldHostComponent implements ControlValueAccessor, OnChange
if (this.disabled && instance.setDisabledState) { if (this.disabled && instance.setDisabledState) {
instance.setDisabledState(true); instance.setDisabledState(true);
} }
// İlk değeri ilet // set initial value
if (this.lastValue !== undefined) { if (this.value !== undefined) {
instance.writeValue?.(this.lastValue); instance.writeValue?.(this.value);
} }
} else { } else {
// Child CVA değilse, formControl input’u varsa köprüle // No CVA -> use form control
if ('formControl' in instance) { if ('formControl' in instance) {
instance.formControl = this.innerControl; instance.formControl = this.innerControl;
// son değeri uygula // apply initial value/disabled state
if (this.lastValue !== undefined) { if (this.value !== undefined) {
this.innerControl.setValue(this.lastValue, { emitEvent: false }); this.innerControl.setValue(this.value, { emitEvent: false });
} }
this.innerControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(v => this.onChange(v)); this.innerControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(v => this.onChange(v));
this.innerControl.disabled ? null : (this.disabled && this.innerControl.disable({ emitEvent: false })); this.innerControl.disabled ? null : (this.disabled && this.innerControl.disable({ emitEvent: false }));
} else {
// Son çare: valueChange EventEmitter’ı varsa bağla (konvansiyonel)
if ('valueChange' in instance && instance['valueChange']?.subscribe) {
instance['valueChange'].pipe(takeUntilDestroyed(this.destroyRef)).subscribe((v: any) => this.onChange(v));
}
} }
} }
} }
@ -108,10 +103,8 @@ export class DynamicFieldHostComponent implements ControlValueAccessor, OnChange
return obj && typeof obj.writeValue === 'function' && typeof obj.registerOnChange === 'function'; return obj && typeof obj.writeValue === 'function' && typeof obj.registerOnChange === 'function';
} }
/* --------- CVA (wrapper) --------- */
writeValue(obj: any): void { writeValue(obj: any): void {
this.lastValue = obj; this.value = obj;
if (!this.componentRef) return; if (!this.componentRef) return;
const inst: any = this.componentRef.instance as controlValueAccessorLike & acceptsFormControl; const inst: any = this.componentRef.instance as controlValueAccessorLike & acceptsFormControl;

10
npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field.component.html

@ -38,15 +38,7 @@
} @else if (field().type === 'checkbox') { } @else if (field().type === 'checkbox') {
<!-- Checkbox --> <!-- Checkbox -->
<div class="form-group form-check"> <div class="form-group form-check">
<input <abp-checkbox [label]="field().label" formControlName="value" [id]="field().key" />
type="checkbox"
[id]="field().key"
formControlName="value"
[class.is-invalid]="isInvalid"
class="form-check-input">
<label class="form-check-label" [for]="field().key">
{{ field().label | abpLocalization }}
</label>
@if (isInvalid) { @if (isInvalid) {
<ng-container [ngTemplateOutlet]="errorTemplate"/> <ng-container [ngTemplateOutlet]="errorTemplate"/>
} }

20
npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field.component.ts

@ -16,7 +16,6 @@ import {
FormControl, FormControl,
FormControlName, FormControlName,
FormGroupDirective, FormGroupDirective,
FormsModule,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
NgControl, NgControl,
FormGroup, FormGroup,
@ -25,6 +24,7 @@ import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NgTemplateOutlet } from '@angular/common'; import { NgTemplateOutlet } from '@angular/common';
import { LocalizationPipe } from '@abp/ng.core'; import { LocalizationPipe } from '@abp/ng.core';
import { FormCheckboxComponent } from '@abp/ng.theme.shared';
export const ABP_DYNAMIC_FORM_FIELD = new InjectionToken<DynamicFormFieldComponent>('AbpDynamicFormField'); export const ABP_DYNAMIC_FORM_FIELD = new InjectionToken<DynamicFormFieldComponent>('AbpDynamicFormField');
@ -40,12 +40,12 @@ const DYNAMIC_FORM_FIELD_CONTROL_VALUE_ACCESSOR = {
styleUrls: ['./dynamic-form-field.component.scss'], styleUrls: ['./dynamic-form-field.component.scss'],
providers: [ providers: [
{ provide: ABP_DYNAMIC_FORM_FIELD, useExisting: DynamicFormFieldComponent }, { provide: ABP_DYNAMIC_FORM_FIELD, useExisting: DynamicFormFieldComponent },
DYNAMIC_FORM_FIELD_CONTROL_VALUE_ACCESSOR DYNAMIC_FORM_FIELD_CONTROL_VALUE_ACCESSOR,
], ],
host: { class: 'abp-dynamic-form-field' }, host: { class: 'abp-dynamic-form-field' },
exportAs: 'abpDynamicFormField', exportAs: 'abpDynamicFormField',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgTemplateOutlet, LocalizationPipe, ReactiveFormsModule], imports: [NgTemplateOutlet, LocalizationPipe, ReactiveFormsModule, FormCheckboxComponent],
}) })
export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor { export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor {
field = input.required<FormFieldConfig>(); field = input.required<FormFieldConfig>();
@ -59,7 +59,7 @@ export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor {
constructor() { constructor() {
this.fieldFormGroup = this.formBuilder.group({ this.fieldFormGroup = this.formBuilder.group({
value: [{ value: '' }] value: [{ value: '' }],
}); });
} }
@ -97,7 +97,7 @@ export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor {
get isInvalid(): boolean { get isInvalid(): boolean {
if (this.control) { if (this.control) {
return (this.control.invalid && (this.control.dirty || this.control.touched)); return this.control.invalid && (this.control.dirty || this.control.touched);
} }
return false; return false;
} }
@ -106,7 +106,9 @@ export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor {
if (this.control && this.control.errors) { if (this.control && this.control.errors) {
const errorKeys = Object.keys(this.control.errors); const errorKeys = Object.keys(this.control.errors);
return errorKeys.map(key => { return errorKeys.map(key => {
const validator = this.field().validators.find(v => v.type.toLowerCase() === key.toLowerCase()); const validator = this.field().validators.find(
v => v.type.toLowerCase() === key.toLowerCase(),
);
console.log(this.field().validators, key); console.log(this.field().validators, key);
if (validator && validator.message) { if (validator && validator.message) {
return validator.message; return validator.message;
@ -114,8 +116,10 @@ export class DynamicFormFieldComponent implements OnInit, ControlValueAccessor {
// Fallback error messages // Fallback error messages
if (key === 'required') return `${this.field().label} is required`; if (key === 'required') return `${this.field().label} is required`;
if (key === 'email') return 'Please enter a valid email address'; if (key === 'email') return 'Please enter a valid email address';
if (key === 'minlength') return `Minimum length is ${this.control.errors[key].requiredLength}`; if (key === 'minlength')
if (key === 'maxlength') return `Maximum length is ${this.control.errors[key].requiredLength}`; return `Minimum length is ${this.control.errors[key].requiredLength}`;
if (key === 'maxlength')
return `Maximum length is ${this.control.errors[key].requiredLength}`;
return `${this.field().label} is invalid due to ${key} validation.`; return `${this.field().label} is invalid due to ${key} validation.`;
}); });
} }

4
npm/ng-packs/packages/components/dynamic-form/src/dynamic-form.models.ts

@ -1,3 +1,5 @@
import { Type } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
export interface FormFieldConfig { export interface FormFieldConfig {
key: string; key: string;
@ -12,7 +14,7 @@ export interface FormFieldConfig {
conditionalLogic?: ConditionalRule[]; conditionalLogic?: ConditionalRule[];
order?: number; order?: number;
gridSize?: number; gridSize?: number;
component?: any; component?: Type<ControlValueAccessor>;
} }
export interface ValidatorConfig { export interface ValidatorConfig {

Loading…
Cancel
Save