libs/core/devtools/dynamic-form-editor/src/lib/dynamic-form-editor/dynamic-form-preview/dynamic-form-preview.component.ts
| changeDetection | ChangeDetectionStrategy.Eager |
| host | { |
| providers |
containerWidth
|
| selector | taly-dynamic-form-preview |
| standalone | true |
| imports | |
| styleUrls | ./dynamic-form-preview.component.scss |
| templateUrl | ./dynamic-form-preview.component.html |
Properties |
Methods |
Inputs |
Outputs |
constructor()
|
| canEdit | |
Type : boolean
|
|
Default value : false
|
|
| highlightedPosition | |
Type : number | undefined
|
|
Default value : undefined
|
|
| existingFormGroup | |
Type : FormGroup | undefined
|
|
|
Inherited from
InternalDfFormComponent
|
|
|
This optional input allows it to hand over a formGroup that the Dynamic Form should use. It will then add the formControls of the fields directly to this formGroup, instead of creating a new one. |
|
| formConfig | |
Type : BaseDynamicFormConfiguration | undefined
|
|
Default value : undefined
|
|
|
Inherited from
InternalDfFormComponent
|
|
| formRawValue | |
Type : Record<string, unknown> | undefined
|
|
Default value : undefined
|
|
|
Inherited from
InternalDfFormComponent
|
|
| id | |
Type : string
|
|
| Required : true | |
|
Inherited from
InternalDfFormComponent
|
|
| transformedValidationConfiguration | |
Type : ValidationConfigItem[]
|
|
|
Inherited from
InternalDfFormComponent
|
|
|
|
| formfieldAdded | |
Type : { index: number; formfieldType: string }
|
|
| formfieldMoved | |
Type : { previousIndex: number; currentIndex: number }
|
|
| formEvent | |
Type : DfEventPayload
|
|
|
Inherited from
InternalDfFormComponent
|
|
| formGroupChange | |
Type : UntypedFormGroup
|
|
|
Inherited from
InternalDfFormComponent
|
|
| formRawValue | |
Type : Record<string, unknown> | undefined
|
|
|
Inherited from
InternalDfFormComponent
|
|
| formValid | |
Type : boolean
|
|
|
Inherited from
InternalDfFormComponent
|
|
| itemDropped | ||||||
itemDropped(event: CdkDragDrop<Type<DfFormfieldComponent>>)
|
||||||
|
Parameters :
Returns :
void
|
| determineLayout | ||||||
determineLayout(formFieldConfig?: BaseDynamicFormConfiguration<DfBaseConfig>)
|
||||||
|
Inherited from
InternalDfFormComponent
|
||||||
|
Parameters :
Returns :
void
|
| getColumnClassList | |||||||||
getColumnClassList(layout: DfFormLayout, config: DfBaseConfig[])
|
|||||||||
|
Inherited from
InternalDfFormComponent
|
|||||||||
|
Parameters :
Returns :
any
|
| updateFormConfig | ||||||
updateFormConfig(newFormConfig?: BaseDynamicFormConfiguration)
|
||||||
|
Inherited from
InternalDfFormComponent
|
||||||
|
This tears down the old form and renders a new one, if a configuration is given
Parameters :
Returns :
void
|
| Readonly containerWidth |
Type : unknown
|
Default value : signal(Infinity)
|
| formFields |
Type : unknown
|
Default value : viewChildren(DfFormfieldComponent, { read: ElementRef })
|
| columnClassList |
Type : unknown
|
Default value : signal<string[]>([])
|
|
Inherited from
InternalDfFormComponent
|
| containerLayout |
Type : unknown
|
Default value : signal<ContainerLayout>(ContainerLayout.SingleColumn)
|
|
Inherited from
InternalDfFormComponent
|
| dynamicFormFields |
Type : QueryList<DfFormfieldComponent>
|
Decorators :
@ViewChildren('dynamicFormField')
|
|
Inherited from
InternalDfFormComponent
|
| formDataToBeRestoredAfterRendering |
Type : Record<string | unknown> | undefined
|
Default value : undefined
|
|
Inherited from
InternalDfFormComponent
|
| formInitFinished |
Type : unknown
|
Default value : outputFromObservable(
toObservable(this.renderingInProgress).pipe(map((renderingInProgress) => !renderingInProgress))
)
|
|
Inherited from
InternalDfFormComponent
|
|
formInitFinished will be triggered, once all components within the dynamic form have finished their initialization. |
| isRetailChannel |
Type : unknown
|
Default value : inject(CHANNEL_TOKEN) === CHANNEL.RETAIL
|
|
Inherited from
InternalDfFormComponent
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
DfFormfieldComponent,
DfFormfieldModule,
FORM_CONTAINER_WIDTH,
InternalDfFormComponent
} from '@allianz/taly-core/dynamic-form';
import { CommonModule } from '@angular/common';
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
ElementRef,
inject,
input,
output,
signal,
Type,
viewChildren
} from '@angular/core';
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop';
@Component({
selector: 'taly-dynamic-form-preview',
templateUrl: './dynamic-form-preview.component.html',
styleUrls: ['./dynamic-form-preview.component.scss'],
imports: [CommonModule, FormsModule, ReactiveFormsModule, DfFormfieldModule, DragDropModule],
host: {
'[class.is-retail]': 'isRetailChannel'
},
providers: [
{
provide: FORM_CONTAINER_WIDTH,
useFactory: (component: DynamicFormPreviewComponent) => component.containerWidth,
deps: [DynamicFormPreviewComponent]
}
],
// This component extends DfFormComponent which can render user-owned form fields.
// It must use Eager change detection so that those components are checked properly.
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
changeDetection: ChangeDetectionStrategy.Eager
})
export class DynamicFormPreviewComponent extends InternalDfFormComponent {
canEdit = input<boolean>(false);
highlightedPosition = input<number | undefined>(undefined);
formfieldMoved = output<{ previousIndex: number; currentIndex: number }>();
formfieldAdded = output<{ index: number; formfieldType: string }>();
formFields = viewChildren(DfFormfieldComponent, { read: ElementRef });
readonly containerWidth = signal(Infinity);
private elementRef = inject(ElementRef);
private destroyRef = inject(DestroyRef);
private currentHighlightedPosition: number | undefined;
constructor() {
super();
effect(() => {
const newPosition = this.highlightedPosition();
const renderingFinished = !this.renderingInProgress();
this.scrollToHighlighted(newPosition, renderingFinished);
});
afterNextRender(() => this.observeContainerWidth());
}
itemDropped(event: CdkDragDrop<Type<DfFormfieldComponent>>) {
if (event.container === event.previousContainer) {
// Formfield is rearranged
const { currentIndex, previousIndex } = event;
if (currentIndex !== previousIndex) {
this.formfieldMoved.emit({ previousIndex, currentIndex });
}
} else if (event.item.data) {
// Formfield is added from outside
this.formfieldAdded.emit({
index: event.currentIndex,
formfieldType: event.item.data.identifier
});
}
}
private observeContainerWidth(): void {
const hostElement = this.elementRef.nativeElement as HTMLElement;
const resizeObserver = new ResizeObserver((entries) => {
this.containerWidth.set(entries[0].contentRect.width);
});
resizeObserver.observe(hostElement);
this.destroyRef.onDestroy(() => resizeObserver.disconnect());
}
private scrollToHighlighted(index?: number, renderingFinished?: boolean) {
if (index === undefined || !this.formFields()?.length) {
return;
}
if (index !== this.currentHighlightedPosition) {
// Smooth scroll when new formfield is highlighted
this.formFields()[index].nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
this.currentHighlightedPosition = index;
} else if (renderingFinished) {
// Instant scroll while editing the same formfield
this.formFields()[index].nativeElement.scrollIntoView({
behavior: 'instant',
block: 'center'
});
}
}
}
@if (formFieldConfig()) {
<form
id="editable-form"
[ngClass]="containerLayout()"
[formGroup]="form"
cdkDropList
cdkDropListOrientation="mixed"
[cdkDropListDisabled]="!canEdit()"
(cdkDropListDropped)="itemDropped($event)"
>
@for (fieldConfig of formFieldConfig(); track fieldConfig; let i = $index) {
<df-formfield
#dynamicFormField
cdkDrag
[attr.data-render-name]="fieldConfig[0].renderName"
[config]="fieldConfig[0]"
[validationConfigs]="formFieldValidationConfigs()[i]"
[className]="columnClassList()[i]"
[ngClass]="{
retail: isRetailChannel,
draggable: canEdit(),
highlighted: highlightedPosition() === i
}"
[defaultSpacing]="spacing()"
[isRetailChannel]="isRetailChannel"
></df-formfield>
}
</form>
}
./dynamic-form-preview.component.scss
@forward '../../../../../../dynamic-form/src/form/form.component.scss';
@forward '../../../../../../dynamic-form/standalone/src/lib/dynamic-form/dynamic-form.component.scss';
df-formfield {
&.highlighted {
outline: 2px dashed var(--accent-03, #bd417f) !important;
outline-offset: 4px;
}
&.draggable {
outline: 2px dashed var(--interactive-primary, #007ab3);
outline-offset: 4px;
background-color: var(--ui-01, #ffffff);
user-select: none;
cursor: grab;
&:active {
cursor: grabbing;
}
::ng-deep > * {
pointer-events: none;
}
}
}
.cdk-drag-preview {
outline: 4px dashed var(--hover-primary, #007ab3);
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px #33333333, 0 8px 10px 1px #23232324, 0 3px 14px 2px #1f1f1f1f;
}
.cdk-drag-placeholder {
opacity: 0.3;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.cdk-drop-list-dragging {
cursor: grab;
&:active {
cursor: grabbing;
}
}