libs/core/devtools/dynamic-form-editor/src/lib/dynamic-form-editor/dynamic-form-preview/dynamic-form-preview.component.ts

Extends

InternalDfFormComponent

Metadata

Relationships

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

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
  1. The PFE Facade enriches the validation configuration with state subscriptions for state-driven values within the validations. Outside a Building Block Platform Journey, the standalone Dynamic Form handles this potential enrichment.

  2. The enriched validation configuration is forwarded directly to the TALY applyValidationConfig() method, which applies the validators to the actual form controls.

Outputs

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

Methods

itemDropped
itemDropped(event: CdkDragDrop<Type<DfFormfieldComponent>>)
Parameters :
Name Type Optional
event CdkDragDrop<Type<DfFormfieldComponent>> No
Returns : void
determineLayout
determineLayout(formFieldConfig?: BaseDynamicFormConfiguration<DfBaseConfig>)
Inherited from InternalDfFormComponent
Parameters :
Name Type Optional
formFieldConfig BaseDynamicFormConfiguration<DfBaseConfig> Yes
Returns : void
getColumnClassList
getColumnClassList(layout: DfFormLayout, config: DfBaseConfig[])
Inherited from InternalDfFormComponent
Parameters :
Name Type Optional
layout DfFormLayout No
config DfBaseConfig[] No
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 :
Name Type Optional
newFormConfig BaseDynamicFormConfiguration Yes
Returns : void

Properties

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;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""