Browse Source

refactoring

pull/24547/head
erdemcaygor 1 year ago
parent
commit
fbd7aa5aa3
  1. 74
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form-field/dynamic-form-field-host.component.ts
  2. 4
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form.component.html
  3. 31
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form.component.ts
  4. 7
      npm/ng-packs/packages/components/dynamic-form/src/dynamic-form.models.ts

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

@ -1,20 +1,32 @@
import {
Component, Input, ViewChild, ViewContainerRef, OnChanges, SimpleChanges,
ChangeDetectionStrategy, forwardRef, Type, Injector, effect, DestroyRef, inject
Component,
ViewChild,
ViewContainerRef,
OnChanges,
SimpleChanges,
ChangeDetectionStrategy,
forwardRef,
Type,
Injector,
effect,
DestroyRef,
inject,
input,
ChangeDetectorRef,
} from '@angular/core';
import {
ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl, ReactiveFormsModule
} from '@angular/forms';
import { CommonModule } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
type MaybeCVA = Partial<ControlValueAccessor> & { setDisabledState?(d: boolean): void };
type WithFormControlInput = { formControl?: FormControl };
type controlValueAccessorLike = Partial<ControlValueAccessor> & { setDisabledState?(d: boolean): void };
type acceptsFormControl = { formControl?: FormControl };
@Component({
selector: 'abp-dynamic-form-field-host',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `<ng-template #vc></ng-template>`,
template: `<ng-template #vcRef></ng-template>`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [{
provide: NG_VALUE_ACCESSOR,
@ -23,39 +35,35 @@ type WithFormControlInput = { formControl?: FormControl };
}]
})
export class DynamicFieldHostComponent implements ControlValueAccessor, OnChanges {
@Input({ required: true }) component!: Type<any>;
@Input() inputs: Record<string, any> = {}; // field, visible, vs.
component = input<Type<any>>();
inputs = input<Record<string, any>>({});
@ViewChild('vc', { read: ViewContainerRef, static: true }) vc!: ViewContainerRef;
@ViewChild('vcRef', { read: ViewContainerRef, static: true }) viewContainerRef!: ViewContainerRef;
private componentRef?: any;
private compRef?: any;
private onChange: (v: any) => void = () => {};
private onTouched: () => void = () => {};
private lastValue: any;
private disabled = false;
// Eğer child CVA değilse kullanmak üzere bir iç kontrol (opsiyonel)
// if child has not implemented ControlValueAccessor. Create form control
private innerControl = new FormControl<any>(null);
readonly destroyRef = inject(DestroyRef);
ngOnChanges(changes: SimpleChanges): void {
if (changes['component']) {
this.createChild();
} else if (this.compRef && changes['inputs']) {
} else if (this.componentRef && changes['inputs']) {
this.applyInputs();
}
}
private createChild() {
this.vc.clear();
if (!this.component) return;
this.compRef = this.vc.createComponent(this.component);
this.viewContainerRef.clear();
if (!this.component()) return;
// inputları ata
this.componentRef = this.viewContainerRef.createComponent(this.component());
this.applyInputs();
// ÇOCUK TIPINI KONTROL ET: CVA mı?
const instance: any = this.compRef.instance as MaybeCVA & WithFormControlInput;
const instance: any = this.componentRef.instance as controlValueAccessorLike & acceptsFormControl;
if (this.isCVA(instance)) {
// Child CVA ise wrapper -> child delege
@ -76,28 +84,27 @@ export class DynamicFieldHostComponent implements ControlValueAccessor, OnChange
if (this.lastValue !== undefined) {
this.innerControl.setValue(this.lastValue, { emitEvent: false });
}
this.innerControl.valueChanges.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 }));
} else {
// Son çare: valueChange EventEmitter’ı varsa bağla (konvansiyonel)
if ('valueChange' in instance && instance['valueChange']?.subscribe) {
instance['valueChange'].subscribe((v: any) => this.onChange(v));
instance['valueChange'].pipe(takeUntilDestroyed(this.destroyRef)).subscribe((v: any) => this.onChange(v));
}
}
}
}
private applyInputs() {
if (!this.compRef) return;
const inst = this.compRef.instance;
if (!this.componentRef) return;
const inst = this.componentRef.instance;
for (const [k, v] of Object.entries(this.inputs ?? {})) {
inst[k] = v;
}
// change detection tetiklenebilir:
this.compRef.changeDetectorRef?.markForCheck?.();
this.componentRef.changeDetectorRef?.markForCheck?.();
}
private isCVA(obj: any): obj is MaybeCVA {
private isCVA(obj: any): obj is controlValueAccessorLike {
return obj && typeof obj.writeValue === 'function' && typeof obj.registerOnChange === 'function';
}
@ -105,9 +112,9 @@ export class DynamicFieldHostComponent implements ControlValueAccessor, OnChange
writeValue(obj: any): void {
this.lastValue = obj;
if (!this.compRef) return;
if (!this.componentRef) return;
const inst: any = this.compRef.instance as MaybeCVA & WithFormControlInput;
const inst: any = this.componentRef.instance as controlValueAccessorLike & acceptsFormControl;
if (this.isCVA(inst)) {
inst.writeValue?.(obj);
@ -116,14 +123,17 @@ export class DynamicFieldHostComponent implements ControlValueAccessor, OnChange
}
}
private onChange: (v: any) => void = () => {};
private onTouched: () => void = () => {};
registerOnChange(fn: any): void { this.onChange = fn; }
registerOnTouched(fn: any): void { this.onTouched = fn; }
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (!this.compRef) return;
if (!this.componentRef) return;
const inst = this.compRef.instance as MaybeCVA & WithFormControlInput;
const inst = this.componentRef.instance as controlValueAccessorLike & acceptsFormControl;
if (this.isCVA(inst) && inst.setDisabledState) {
inst.setDisabledState(isDisabled);

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

@ -4,11 +4,11 @@
@for (field of sortedFields; track field.key) {
<div [ngClass]="'col-md-' + (field.gridSize || 12)">
@if (field.component) {
<app-dynamic-field-host
<abp-dynamic-form-field-host
[component]="field.component"
[inputs]="{ field: field, visible: isFieldVisible(field) }"
formControlName="{{ field.key }}">
</app-dynamic-field-host>
</abp-dynamic-form-field-host>
} @else {
<abp-dynamic-form-field
[field]="field"

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

@ -7,19 +7,14 @@ import {
OnInit,
DestroyRef,
ChangeDetectorRef,
effect,
contentChild,
ContentChildren,
QueryList,
AfterContentInit,
effect
} from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DynamicFormService } from './dynamic-form.service';
import { FormFieldConfig } from './dynamic-form.models';
import { DynamicFormFieldComponent, DynamicInputDirective } from './dynamic-form-field';
import { DynamicFieldHostComponent } from './dynamic-form-field/dynamic-form-field-host.component';
import { ConditionalAction, FormFieldConfig } from './dynamic-form.models';
import { DynamicFormFieldComponent, DynamicFieldHostComponent } from './dynamic-form-field';
@Component({
selector: 'abp-dynamic-form',
@ -32,11 +27,10 @@ import { DynamicFieldHostComponent } from './dynamic-form-field/dynamic-form-fie
CommonModule,
DynamicFormFieldComponent,
ReactiveFormsModule,
DynamicInputDirective,
DynamicFieldHostComponent,
],
})
export class DynamicFormComponent implements OnInit, AfterContentInit {
export class DynamicFormComponent implements OnInit {
fields = input<FormFieldConfig[]>([]);
values = input<Record<string, any>>();
submitButtonText = input<string>('Submit');
@ -47,21 +41,12 @@ export class DynamicFormComponent implements OnInit, AfterContentInit {
private dynamicFormService = inject(DynamicFormService);
readonly destroyRef = inject(DestroyRef);
readonly changeDetectorRef = inject(ChangeDetectorRef);
@ContentChildren(DynamicInputDirective) dynamicInputs: QueryList<DynamicInputDirective>;
dynamicForm!: FormGroup;
fieldVisibility: { [key: string]: boolean } = {};
ngOnInit() {
this.setupFormAndLogic();
effect(() => {
console.log(this.values());
});
}
ngAfterContentInit() {
console.log(this.dynamicInputs.toArray());
}
get sortedFields(): FormFieldConfig[] {
@ -159,18 +144,18 @@ export class DynamicFormComponent implements OnInit, AfterContentInit {
const control = this.dynamicForm.get(fieldKey);
switch (action) {
case 'show':
case ConditionalAction.SHOW:
this.fieldVisibility = { ...this.fieldVisibility, [fieldKey]: shouldApply };
break;
case 'hide':
case ConditionalAction.HIDE:
this.fieldVisibility = { ...this.fieldVisibility, [fieldKey]: !shouldApply };
break;
case 'enable':
case ConditionalAction.ENABLE:
if (control) {
shouldApply ? control.enable() : control.disable();
}
break;
case 'disable':
case ConditionalAction.DISABLE:
if (control) {
shouldApply ? control.disable() : control.enable();
}

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

@ -27,3 +27,10 @@ export interface ConditionalRule {
value: any;
action: 'show' | 'hide' | 'enable' | 'disable';
}
export enum ConditionalAction {
SHOW = 'show',
HIDE = 'hide',
ENABLE = 'enable',
DISABLE = 'disable'
}

Loading…
Cancel
Save