Source

lib/components/crud-field/crud-field.component.ts

import {
  AfterViewInit,
  Component,
  CUSTOM_ELEMENTS_SCHEMA,
  ElementRef,
  HostListener,
  Input,
  OnDestroy,
  OnInit,
  ViewChild,
} from '@angular/core';
import { FormArray, FormControl, FormGroup } from '@angular/forms';
import { AutocompleteTypes, SelectInterface } from '@ionic/core';
import { CrudOperations, OperationKeys } from '@decaf-ts/db-decorators';
import { NgxCrudFormField } from '../../engine/NgxCrudFormField';
import { Dynamic } from '../../engine/decorators';
import { FieldUpdateMode, PossibleInputTypes, RadioOption, SelectOption, StringOrBoolean } from '../../engine/types';
import { ForAngularModule } from '../../for-angular.module';
import {
  IonCheckbox,
  IonIcon,
  IonInput,
  IonItem,
  IonLabel,
  IonRadio,
  IonRadioGroup,
  IonSelect,
  IonSelectOption,
  IonText,
  IonTextarea,
} from '@ionic/angular/standalone';
import { HTML5InputTypes } from '@decaf-ts/ui-decorators';
import { addIcons } from 'ionicons';
import { chevronDownOutline, chevronUpOutline } from 'ionicons/icons';
import { generateRandomValue } from '../../helpers';
import { NgxFormService } from '../../engine/NgxFormService';
import { EventConstants } from '../../engine/constants';

/**
 * @description A dynamic form field component for CRUD operations.
 * @summary The CrudFieldComponent is a versatile form field component that adapts to different
 * input types and CRUD operations. It extends NgxCrudFormField to inherit form handling capabilities
 * and implements lifecycle hooks to properly initialize, render, and clean up. This component
 * supports various input types (text, number, date, select, etc.), validation rules, and styling
 * options, making it suitable for building dynamic forms for create, read, update, and delete
 * operations.
 *
 * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
 * @param {string} name - The field name, used as form control identifier
 * @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
 * @param {string|number|Date} value - The initial value of the field
 * @param {boolean} disabled - Whether the field is disabled
 * @param {string} label - The display label for the field
 * @param {string} placeholder - Placeholder text when field is empty
 * @param {string} format - Format pattern for the field value
 * @param {boolean} hidden - Whether the field should be hidden
 * @param {number|Date} max - Maximum allowed value
 * @param {number} maxlength - Maximum allowed length
 * @param {number|Date} min - Minimum allowed value
 * @param {number} minlength - Minimum allowed length
 * @param {string} pattern - Validation pattern
 * @param {boolean} readonly - Whether the field is read-only
 * @param {boolean} required - Whether the field is required
 * @param {number} step - Step value for number inputs
 * @param {FormGroup} formGroup - The parent form group
 * @param {StringOrBoolean} translatable - Whether field labels should be translated
 *
 * @component CrudFieldComponent
 * @example
 * <ngx-decaf-crud-field
 *   operation="create"
 *   name="firstName"
 *   type="text"
 *   label="<NAME>"
 *   placeholder="<NAME>"
 *   [value]="model.firstName"
 *   [disabled]="model.readOnly">
 *
 *
 * @memberOf module:for-angular
 */
@Dynamic()
@Component({
  standalone: true,
  imports: [
    ForAngularModule,
    IonInput,
    IonItem,
    IonCheckbox,
    IonRadioGroup,
    IonRadio,
    IonSelect,
    IonSelectOption,
    IonLabel,
    IonText,
    IonTextarea,
    IonIcon
  ],
  selector: 'ngx-decaf-crud-field',
  templateUrl: './crud-field.component.html',
  styleUrl: './crud-field.component.scss',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  host: {'[attr.id]': 'uid'},
})
export class CrudFieldComponent extends NgxCrudFormField implements OnInit, OnDestroy, AfterViewInit {

  /**
   * @description The CRUD operation being performed.
   * @summary Specifies which CRUD operation (Create, Read, Update, Delete) the field is being used for.
   * This affects how the field behaves and is rendered. For example, fields might be read-only in
   * 'read' mode but editable in 'create' or 'update' modes.
   *
   * @type {CrudOperations}
   * @memberOf CrudFieldComponent
   */
  @Input({ required: true })
  override operation!: CrudOperations;

  /**
   * @summary  The flat field name used as the form control identifier in immediate parent FormGroup.
   * @description
   * Specifies the name of the field, which is used as the FormControl identifier in immediate parent FormGroup.
   * This value must be unique within the immediate parent FormGroup context and should not contain dots or nesting.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input({ required: true })
  override name!: string;


  /**
   * @summary The full field path used for form control resolution.
   * @description Specifies the hierarchical path of the field, used to resolve its location within the parent FormGroup (or nested FormGroups).
   * It is used as the identifier in the rendered HTML, and may include nesting (e.g., 'address.billing.street') and
   * should match the structure of the data model
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input({ required: true })
  override path!: string;

  /**
   * @description The parent field path, if this field is nested.
   * @summary Specifies the full dot-delimited path of the parent field. This is only set when the field is nested.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  /**
   * @description The parent field path for nested field structures.
   * @summary Specifies the full dot-delimited path of the parent field when this field
   * is part of a nested structure. This is used for hierarchical form organization
   * and proper form control resolution in complex form structures.
   *
   * @type {string}
   * @default ''
   * @memberOf CrudFieldComponent
   */
  @Input()
  override childOf: string = '';

  /**
   * @description The input type of the field.
   * @summary Defines the type of input to render, such as text, number, date, select, etc.
   * This determines which Ionic form component will be used to render the field and how
   * the data will be formatted and validated.
   *
   * @type {PossibleInputTypes}
   * @memberOf CrudFieldComponent
   */
  @Input({ required: true })
  override type!: PossibleInputTypes;

  /**
   * @description The initial value of the field.
   * @summary Sets the initial value of the form field. This can be a string, number, or Date
   * depending on the field type. For select fields, this should match one of the option values.
   *
   * @type {string | number | Date}
   * @default ''
   * @memberOf CrudFieldComponent
   */
  @Input()
  override value: string | number | Date = '';

  /**
   * @description Whether the field is disabled.
   * @summary When true, the field will be rendered in a disabled state, preventing user interaction.
   * Disabled fields are still included in the form model but cannot be edited by the user.
   *
   * @type {boolean}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override disabled?: boolean;

  /**
   * @description The display label for the field.
   * @summary The text label displayed alongside the field to identify it to the user.
   * This label can be translated if the translatable property is set to true.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input({ required: true })
  label!: string;

  /**
   * @description Placeholder text when field is empty.
   * @summary Text that appears in the input when it has no value. This provides a hint to the user
   * about what kind of data is expected. The placeholder disappears when the user starts typing.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input()
  placeholder!: string;

  /**
   * @description Format pattern for the field value.
   * @summary Specifies a format pattern for the field value, which can be used for date formatting,
   * number formatting, or other type-specific formatting requirements.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override format?: string;

  /**
   * @description Whether the field should be hidden.
   * @summary When true, the field will not be visible in the UI but will still be part of the form model.
   * This is useful for fields that need to be included in form submission but should not be displayed to the user.
   *
   * @type {boolean}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override hidden?: boolean;

  /**
   * @description Maximum allowed value for the field.
   * @summary For number inputs, this sets the maximum allowed numeric value.
   * For date inputs, this sets the latest allowed date.
   *
   * @type {number | Date}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override max?: number | Date;

  /**
   * @description Maximum allowed length for text input.
   * @summary For text inputs, this sets the maximum number of characters allowed.
   * This is used for validation and may also be used to limit input in the UI.
   *
   * @type {number}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override maxlength?: number;

  /**
   * @description Minimum allowed value for the field.
   * @summary For number inputs, this sets the minimum allowed numeric value.
   * For date inputs, this sets the earliest allowed date.
   *
   * @type {number | Date}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override min?: number | Date;

  /**
   * @description Minimum allowed length for text input.
   * @summary For text inputs, this sets the minimum number of characters required.
   * This is used for validation to ensure the input meets a minimum length requirement.
   *
   * @type {number}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override minlength?: number;

  /**
   * @description Validation pattern for text input.
   * @summary A regular expression pattern used to validate text input.
   * The input value must match this pattern to be considered valid.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override pattern?: string;

  /**
   * @description Whether the field is read-only.
   * @summary When true, the field will be rendered in a read-only state. Unlike disabled fields,
   * read-only fields are still focusable but cannot be modified by the user.
   *
   * @type {boolean}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override readonly?: boolean;

  /**
   * @description Whether the field is required.
   * @summary When true, the field must have a value for the form to be valid.
   * Required fields are typically marked with an indicator in the UI.
   *
   * @type {boolean}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override required?: boolean;

  /**
   * @description Step value for number inputs.
   * @summary For number inputs, this sets the increment/decrement step when using
   * the up/down arrows or when using a range slider.
   *
   * @type {number}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override step?: number;

  /**
   * @description Field name for equality validation comparison.
   * @summary Specifies another field name that this field's value must be equal to for validation.
   * This is commonly used for password confirmation fields or other scenarios where
   * two fields must contain the same value.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override equals?: string;

  /**
   * @description Field name for inequality validation comparison.
   * @summary Specifies another field name that this field's value must be different from for validation.
   * This is used to ensure that two fields do not contain the same value, which might be
   * required for certain business rules or security constraints.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override different?: string;

  /**
   * @description Field name for less-than validation comparison.
   * @summary Specifies another field name that this field's value must be less than for validation.
   * This is commonly used for date ranges, numeric ranges, or other scenarios where
   * one field must have a smaller value than another.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override lessThan?: string;

  /**
   * @description Field name for less-than-or-equal validation comparison.
   * @summary Specifies another field name that this field's value must be less than or equal to
   * for validation. This provides inclusive upper bound validation for numeric or date comparisons.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override lessThanOrEqual?: string;

  /**
   * @description Field name for greater-than validation comparison.
   * @summary Specifies another field name that this field's value must be greater than for validation.
   * This is commonly used for date ranges, numeric ranges, or other scenarios where
   * one field must have a larger value than another.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override greaterThan?: string;

  /**
   * @description Field name for greater-than-or-equal validation comparison.
   * @summary Specifies another field name that this field's value must be greater than or equal to
   * for validation. This provides inclusive lower bound validation for numeric or date comparisons.
   *
   * @type {string | undefined}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override greaterThanOrEqual?: string;

  /**
   * @description Number of columns for textarea inputs.
   * @summary For textarea inputs, this sets the visible width of the text area in average character widths.
   * This is used alongside rows to define the dimensions of the textarea.
   *
   * @type {number}
   * @memberOf CrudFieldComponent
   */
  @Input()
  cols?: number;

  /**
   * @description Number of rows for textarea inputs.
   * @summary For textarea inputs, this sets the visible height of the text area in lines of text.
   * This is used alongside cols to define the dimensions of the textarea.
   *
   * @type {number}
   * @memberOf CrudFieldComponent
   */
  @Input()
  rows?: number;

  /**
   * @description Alignment of the field content.
   * @summary Controls the horizontal alignment of the field content.
   * This affects how the content is positioned within the field container.
   *
   * @type {'start' | 'center'}
   * @memberOf CrudFieldComponent
   */
  @Input()
  alignment?: 'start' | 'center';

  /**
   * @description Initial checked state for checkbox or toggle inputs.
   * @summary For checkbox or toggle inputs, this sets the initial checked state.
   * When true, the checkbox or toggle will be initially checked.
   *
   * @type {boolean}
   * @memberOf CrudFieldComponent
   */
  @Input()
  checked?: boolean;

  /**
   * @description Justification of items within the field.
   * @summary Controls how items are justified within the field container.
   * This is particularly useful for fields with multiple elements, such as radio groups.
   *
   * @type {'start' | 'end' | 'space-between'}
   * @memberOf CrudFieldComponent
   */
  @Input()
  justify?: 'start' | 'end' | 'space-between';

  /**
   * @description Text for the cancel button in select inputs.
   * @summary For select inputs with a cancel button, this sets the text displayed on the cancel button.
   * This is typically used in select dialogs to provide a way for users to dismiss the selection without making a change.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input()
  cancelText?: string;

  /**
   * @description Interface style for select inputs.
   * @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
   * This determines how the select options are presented to the user.
   *
   * @type {SelectInterface}
   * @memberOf CrudFieldComponent
   */
  @Input()
  interface: SelectInterface = 'popover';

  /**
   * @description Options for select or radio inputs.
   * @summary Provides the list of options for select or radio inputs. Each option can have a value and a label.
   * This is used to populate the dropdown or radio group with choices.
   *
   * @type {SelectOption[] | RadioOption[]}
   * @memberOf CrudFieldComponent
   */
  @Input()
  options!: SelectOption[] | RadioOption[];

  /**
   * @description Mode of the field.
   * @summary Specifies the visual mode of the field, such as 'ios' or 'md'.
   * This affects the styling and appearance of the field to match the platform style.
   *
   * @type {'ios' | 'md'}
   * @memberOf CrudFieldComponent
   */
  @Input()
  mode?: 'ios' | 'md';

  /**
   * @description Spellcheck attribute for text inputs.
   * @summary Enables or disables spellchecking for text inputs.
   * When true, the browser will check the spelling of the input text.
   *
   * @type {boolean}
   * @default false
   * @memberOf CrudFieldComponent
   */
  @Input()
  spellcheck: boolean = false;

  /**
   * @description Input mode for text inputs.
   * @summary Hints at the type of data that might be entered by the user while editing the element.
   * This can affect the virtual keyboard layout on mobile devices.
   *
   * @type {'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'}
   * @default 'none'
   * @memberOf CrudFieldComponent
   */
  @Input()
  inputmode:
    | 'none'
    | 'text'
    | 'tel'
    | 'url'
    | 'email'
    | 'numeric'
    | 'decimal'
    | 'search' = 'none';

  /**
   * @description Autocomplete behavior for the field.
   * @summary Specifies whether and how the browser should automatically complete the input.
   * This can improve user experience by suggesting previously entered values.
   *
   * @type {AutocompleteTypes}
   * @default 'off'
   * @memberOf CrudFieldComponent
   */
  @Input()
  autocomplete: AutocompleteTypes = 'off';

  /**
   * @description Fill style for the field.
   * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
   * This affects the border and background of the field.
   *
   * @type {'outline' | 'solid'}
   * @default 'outline'
   * @memberOf CrudFieldComponent
   */
  @Input()
  fill: 'outline' | 'solid' = 'outline';

  /**
   * @description Placement of the label relative to the field.
   * @summary Specifies where the label should be placed relative to the field.
   * Options include 'start', 'end', 'floating', 'stacked', and 'fixed'.
   *
   * @type {'start' | 'end' | 'floating' | 'stacked' | 'fixed'}
   * @default 'floating'
   * @memberOf CrudFieldComponent
   */
  @Input()
  labelPlacement: 'start' | 'end' | 'floating' | 'stacked' | 'fixed' =
    'floating';

  /**
   * @description Update mode for the field.
   * @summary Determines when the field value should be updated in the form model.
   * Options include 'change', 'blur', and 'submit'.
   *
   * @type {FieldUpdateMode}
   * @default 'change'
   * @memberOf CrudFieldComponent
   */
  @Input()
  updateOn: FieldUpdateMode = 'change';

  /**
   * @description Reference to the field component.
   * @summary Provides a reference to the field component element, allowing direct access to its properties and methods.
   *
   * @type {ElementRef}
   * @memberOf CrudFieldComponent
   */
  @ViewChild('component', { read: ElementRef })
  override component!: ElementRef;

  /**
   * @description Parent form group.
   * @summary References the parent form group to which this field belongs.
   * This is necessary for integrating the field with Angular's reactive forms.
   *
   * @type {FormGroup}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override formGroup: FormGroup | undefined;


  /**
   * @description Angular FormControl instance for this field.
   * @summary The specific FormControl instance that manages this field's state, validation,
   * and value. This provides direct access to Angular's reactive forms functionality
   * for this individual field within the broader form structure.
   *
   * @type {FormControl}
   * @memberOf CrudFieldComponent
   */
  @Input()
  override formControl!: FormControl;

  /**
   * @description Indicates if this field supports multiple values.
   * @summary When true, this field can handle multiple values, typically used in
   * multi-select scenarios or when the field is part of a form array structure
   * that allows multiple entries of the same field type.
   *
   * @type {boolean}
   * @default false
   * @memberOf CrudFieldComponent
   */
  @Input()
  override multiple: boolean = false;

  /**
   * @description Unique identifier for the current record.
   * @summary A unique identifier for the current record being displayed or manipulated.
   * This is typically used in conjunction with the primary key for operations on specific records.
   *
   * @type {string | number}
   */
  @Input()
  override uid: string = generateRandomValue(12);


  /**
   * @description Translatability of field labels.
   * @summary Indicates whether the field labels should be translated based on the current language settings.
   * This is useful for applications supporting multiple languages.
   *
   * @type {StringOrBoolean}
   * @default true
   * @memberOf CrudFieldComponent
   */
  @Input()
  translatable: StringOrBoolean = true;



  /**
   * @description Index of the currently active form group in a form array.
   * @summary When working with multiple form groups (form arrays), this indicates
   * which form group is currently active or being edited. This is used to manage
   * focus and data binding in multi-entry scenarios.
   *
   * @type {number}
   * @default 0
   * @memberOf CrudFieldComponent
   */
  @Input()
  activeFormGroup: number = 0;

  /**
   * @description FormArray containing multiple form groups for this field.
   * @summary When this field is part of a multi-entry structure, this FormArray
   * contains all the form groups. This enables management of multiple instances
   * of the same field structure within a single form.
   *
   * @type {FormArray}
   * @memberOf CrudFieldComponent
   */
  formGroupArray!: FormArray;

  /**
   * @description Primary key field name for uniqueness validation.
   * @summary Specifies the field name that serves as the primary key for uniqueness
   * validation within form arrays. This is used to prevent duplicate entries
   * and ensure data integrity in multi-entry forms.
   *
   * @type {string}
   * @memberOf CrudFieldComponent
   */
  @Input()
  pk!: string;


  /**
   * @description Gets the currently active form group based on context.
   * @summary Returns the appropriate FormGroup based on whether this field supports
   * multiple values. For single-value fields, returns the main form group.
   * For multi-value fields, returns the form group at the active index from the parent FormArray.
   *
   * @returns {FormGroup} The currently active FormGroup for this field
   * @memberOf CrudFieldComponent
   */
  get getActiveFormGroup(): FormGroup {
    const formGroup = this.formGroup as FormGroup;
    return this.multiple
      ? ((formGroup.parent as FormArray)?.at(this.activeFormGroup) as FormGroup)
      : formGroup;

  }


  /**
   * @description Component initialization lifecycle method.
   * @summary Initializes the field component based on the operation type and field configuration.
   * For READ and DELETE operations, removes the form group to make fields read-only.
   * For other operations, sets up icons, configures multi-value support if needed,
   * and sets default values for radio buttons if no value is provided.
   *
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  ngOnInit(): void {
    if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
      this.formGroup = undefined;
    } else {
      addIcons({chevronDownOutline, chevronUpOutline})

      if(this.multiple) {
        this.formGroup = this.getActiveFormGroup as FormGroup;
        this.formGroupArray = this.formGroup.parent as FormArray;
      }

      if (this.type === HTML5InputTypes.RADIO && !this.value)
        this.formGroup?.get(this.name)?.setValue(this.options[0].value); // TODO: migrate to RenderingEngine
    }
  }


  /**
   * @description Component after view initialization lifecycle method.
   * @summary Calls the parent afterViewInit method for READ and DELETE operations.
   * This ensures proper initialization of read-only fields that don't require
   * form functionality but still need view setup.
   *
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  ngAfterViewInit() {
    if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
      super.afterViewInit();
  }

  /**
   * @description Component cleanup lifecycle method.
   * @summary Performs cleanup operations for READ and DELETE operations by calling
   * the parent onDestroy method. This ensures proper resource cleanup for
   * read-only field components.
   *
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  ngOnDestroy(): void {
    if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
      this.onDestroy();
  }

  /**
   * @description Handles fieldset group creation events from parent fieldsets.
   * @summary Processes events triggered when a new group needs to be added to a fieldset.
   * Validates the current form group, checks for uniqueness if applicable, and either
   * creates a new group or provides validation feedback. Updates the active form group
   * and resets the field for new input after successful creation.
   *
   * @param {CustomEvent} event - The fieldset create group event containing group details
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  @HostListener('window:fieldsetAddGroupEvent', ['$event'])
  handleFieldsetCreateGroupEvent(event: CustomEvent) {
    event.stopImmediatePropagation();
    const { parent, component, index, operation } = event.detail;
    const formGroup = this.formGroup as FormGroup;
    const parentFormGroup = this.formGroup?.parent as FormArray;
    const isValid = NgxFormService.validateFields(formGroup as FormGroup);
    const indexToCheck = operation === OperationKeys.CREATE ?
      index === 0 ? index : parentFormGroup.length - 1 : index - 1;

    const isUnique = NgxFormService.isUniqueOnGroup(formGroup, indexToCheck, operation || OperationKeys.CREATE);
    event = new CustomEvent(EventConstants.FIELDSET_ADD_GROUP, {
      detail: {isValid: isValid && isUnique, value: formGroup.value, formGroup: parentFormGroup, formService: NgxFormService},
    });
    component.dispatchEvent(event);
    if(isValid && isUnique) {
      const newIndex = parentFormGroup.length;

      if(operation === OperationKeys.CREATE) {
        NgxFormService.addGroupToParent(parentFormGroup?.parent as FormGroup, parent, newIndex);
        this.activeFormGroup = newIndex;
      } else {
        this.activeFormGroup = newIndex - 1;
      }
      this.formGroup = this.getActiveFormGroup;
      // NgxFormService.reset(this.formGroup as FormGroup);
      this.formControl = (this.formGroup as FormGroup).get(this.name) as FormControl;
      // NgxFormService.reset(this.formControl);
      // this.component.nativeElement.setFocus();
    } else {
      if(isUnique)
        this.component.nativeElement.setFocus();
    }
  }


  /**
   * @description Handles fieldset group update events from parent fieldsets.
   * @summary Processes events triggered when an existing group needs to be updated.
   * Updates the active form group index and refreshes the form group and form control
   * references to point to the group being edited.
   *
   * @param {CustomEvent} event - The fieldset update group event containing update details
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  @HostListener('window:fieldsetUpdateGroupEvent', ['$event'])
  handleFieldsetUpdateGroupEvent(event: CustomEvent): void {
    const {index} = event.detail;
    this.activeFormGroup = index;
    this.formGroup = this.getActiveFormGroup;
    this.formControl = this.formGroup.get(this.name) as FormControl;
  }


  /**
   * @description Handles fieldset group removal events from parent fieldsets.
   * @summary Processes events triggered when a group needs to be removed from a fieldset.
   * Removes the specified group from the form array, updates the active form group index,
   * and refreshes the form references. Dispatches a confirmation event back to the component.
   *
   * @param {CustomEvent} event - The fieldset remove group event containing removal details
   * @returns {void}
   * @memberOf CrudFieldComponent
   */
  @HostListener('window:fieldsetRemoveGroupEvent', ['$event'])
  handleFieldsetRemoveGroupEvent(event: CustomEvent): void {
    const { component, index } = event.detail;
    const formArray = this.formGroup?.parent as FormArray;
    formArray.removeAt(index);
    this.activeFormGroup = formArray.length === 1 ? 0 : formArray.length - 1;
    this.formGroup = this.getActiveFormGroup;
    this.formControl = this.formGroup.get(this.name) as FormControl;
    this.formGroupArray = formArray
    event = new CustomEvent(EventConstants.FIELDSET_REMOVE_GROUP, {
      detail: {value: true},
    });
    component.dispatchEvent(event);
  }
}