File

libs/core/form-tracking/src/lib/form-tracking.ts

Extends

AbstractControl

Index

Properties

Properties

talyTrackingApplied (Optional)
Type boolean
import {
  TalyAbstractControl,
  getNativeElement$
} from '@allianz/taly-acl/input-element-injector-directive';
import { AbstractControl, isFormArray, isFormControl, isFormGroup } from '@angular/forms';
import { Observable, debounceTime, filter, fromEvent, merge, switchMap, takeUntil } from 'rxjs';

export interface TalyAbstractTrackingControl extends AbstractControl {
  talyTrackingApplied?: boolean;
}

export type PushEventFunction = (id: string, value: unknown) => void;

export function trackForm(
  controlOrGroup: AbstractControl,
  trackingIdPrefix: string,
  untilDestroyed$: Observable<unknown>,
  pushEvent: PushEventFunction
): void {
  setupFormTracking(controlOrGroup, trackingIdPrefix, untilDestroyed$, pushEvent);

  if (isFormControl(controlOrGroup)) {
    return;
  }

  // Subscribe to detect new form controls being added or removed
  controlOrGroup.statusChanges.pipe(takeUntil(untilDestroyed$), debounceTime(300)).subscribe(() => {
    setupFormTracking(controlOrGroup, trackingIdPrefix, untilDestroyed$, pushEvent);
  });
}

function setupFormTracking(
  controlOrGroup: AbstractControl,
  trackingIdPrefix: string,
  untilDestroyed$: Observable<unknown>,
  pushEvent: PushEventFunction,
  parentPath: string[] = []
): void {
  if (isFormControl(controlOrGroup)) {
    if (!(controlOrGroup as TalyAbstractTrackingControl).talyTrackingApplied) {
      trackControl(
        controlOrGroup,
        trackingIdPrefix,
        parentPath.join('/'),
        untilDestroyed$,
        pushEvent
      );
      (controlOrGroup as TalyAbstractTrackingControl).talyTrackingApplied = true;
    }
  } else if (isFormGroup(controlOrGroup) || isFormArray(controlOrGroup)) {
    const entries = Object.entries(controlOrGroup.controls);

    for (const [path, nestedControl] of entries) {
      const currentPath = [...parentPath, path];
      setupFormTracking(nestedControl, trackingIdPrefix, untilDestroyed$, pushEvent, currentPath);
    }
  }
}

function trackControl(
  control: AbstractControl,
  trackingIdPrefix: string,
  controlName: string,
  untilDestroyed$: Observable<unknown>,
  pushEvent: PushEventFunction
): void {
  const getVisibleNativeElement$ = getNativeElement$(control).pipe(filter(Boolean));

  control.statusChanges
    ?.pipe(takeUntil(merge(getVisibleNativeElement$, untilDestroyed$)), debounceTime(300))
    .subscribe(() => {
      pushTrackingEvents(control, trackingIdPrefix, controlName, pushEvent);
    });

  getVisibleNativeElement$
    .pipe(
      switchMap((nativeFormElement) => fromEvent(nativeFormElement, 'blur')),
      takeUntil(untilDestroyed$)
    )
    .subscribe(() => {
      pushTrackingEvents(control, trackingIdPrefix, controlName, pushEvent);
    });
}

function pushTrackingEvents(
  control: TalyAbstractControl,
  trackingIdPrefix: string,
  controlName: string,
  pushEvent: (id: string, value: unknown) => void
): void {
  const baseEventName = `automaticTALYTracking.${trackingIdPrefix}.${controlName}`;

  pushEvent(`${baseEventName}.value`, getTrackingValue(control));
  pushEvent(`${baseEventName}.valid`, control.valid);
  pushEvent(`${baseEventName}.dirty`, control.dirty);
  pushEvent(`${baseEventName}.touched`, control.touched);
  pushEvent(`${baseEventName}.errors`, control.errors);
}

function getTrackingValue(control: TalyAbstractControl): unknown {
  const nativeElement = getNativeElement$(control).value;
  const trackingValue =
    (nativeElement as HTMLInputElement)?.type === 'password' ? 'PROTECTED' : control.value;
  return trackingValue;
}

results matching ""

    No results matching ""