Source

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

import {
  Component,
  ElementRef,
  EventEmitter,
  inject,
  Input,
  OnDestroy,
  OnInit,
  Output,
  ViewChild,
} from '@angular/core';
import { Location } from '@angular/common';
import { FormGroup } from '@angular/forms';
import { IFormElement } from '../../engine/interfaces';
import { NgxFormService } from '../../engine/NgxFormService';
import { CrudFormEvent, Dynamic, EventConstants, FieldUpdateMode, HandlerLike, HTMLFormTarget, RenderedModel } from '../../engine';
import { CrudFormOptions } from './types';
import { CrudOperations, OperationKeys } from '@decaf-ts/db-decorators';
import { DefaultFormReactiveOptions } from './constants';
import { ForAngularModule, getLogger } from '../../for-angular.module';
import { IonIcon } from '@ionic/angular/standalone';
import { Model } from '@decaf-ts/decorator-validation';
import { Logger } from '@decaf-ts/logging';
import { generateRandomValue } from '../../helpers';


/**
 * @component CrudFormComponent
 * @example <ngx-decaf-crud-form
 *   action="create"
 *   operation="create"
 *   formGroup="formGroup"
 *   rendererId="rendererId"
 *   submitEvent="submitEvent"
 *   target="_self"
 *   method="event">
 * </ngx-decaf-crud-form>
 *
 * @param {string} action - The action to be performed (create, read, update, delete)
 * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
 * @param {FormGroup} formGroup - The form group
 * @param {string} rendererId - The renderer id
 * @param {SubmitEvent} submitEvent - The submit event
 * @param {string} target - The target
 * @param {string} method - The method
 */
@Dynamic()
@Component({
  standalone: true,
  selector: 'ngx-decaf-crud-form',
  templateUrl: './crud-form.component.html',
  styleUrls: ['./crud-form.component.scss'],
  imports: [ForAngularModule, IonIcon],
  host: {'[attr.id]': 'uid'},
})
export class CrudFormComponent implements OnInit, IFormElement, OnDestroy, RenderedModel {

  /**
   * @description Repository model for data operations.
   * @summary The data model repository that this component will use for CRUD operations.
   * This provides a connection to the data layer for retrieving and manipulating data.
   *
   * @type {Model| undefined}
   * @memberOf CrudFormComponent
   */
  @Input()
  model!: Model | undefined;

  /**
   * @description Field update trigger mode for form validation.
   * @summary Determines when form field validation should be triggered. Options include
   * 'change', 'blur', or 'submit'. This affects the user experience by controlling
   * when validation feedback is shown to the user during form interaction.
   *
   * @type {FieldUpdateMode}
   * @default 'change'
   * @memberOf CrudFormComponent
   */
  @Input()
  updateOn: FieldUpdateMode = 'change';

  /**
   * @description Reference to the reactive form DOM element.
   * @summary ViewChild reference that provides direct access to the form's DOM element.
   * This enables programmatic manipulation of the form element and access to native
   * HTML form properties and methods when needed.
   *
   * @type {ElementRef}
   * @memberOf CrudFormComponent
   */
  @ViewChild('reactiveForm', { static: false, read: ElementRef })
  component!: ElementRef;

  /**
   * @description Form submission target specification.
   * @summary Specifies where to display the response after form submission, similar
   * to the HTML form target attribute. Options include '_self', '_blank', '_parent',
   * '_top', or a named frame. Controls the browser behavior for form responses.
   *
   * @type {HTMLFormTarget}
   * @default '_self'
   * @memberOf CrudFormComponent
   */
  @Input()
  target: HTMLFormTarget = '_self';

  /**
   * @description HTTP method or submission strategy for the form.
   * @summary Defines how the form should be submitted. 'get' and 'post' correspond
   * to standard HTTP methods for traditional form submission, while 'event' uses
   * Angular event-driven submission for single-page application workflows.
   *
   * @type {'get' | 'post' | 'event'}
   * @default 'event'
   * @memberOf CrudFormComponent
   */
  @Input()
  method: 'get' | 'post' | 'event' = 'event';

  /**
   * @description Configuration options for the CRUD form behavior.
   * @summary Contains various configuration settings that control form rendering,
   * validation, and behavior. These options are merged with default settings
   * during component initialization to customize the form's functionality.
   *
   * @type {CrudFormOptions}
   * @memberOf CrudFormComponent
   */
  @Input()
  options!: CrudFormOptions;

  /**
   * @description Optional action identifier for form submission context.
   * @summary Specifies a custom action name that will be included in the submit event.
   * If not provided, defaults to the standard submit event constant. Used to
   * distinguish between different types of form submissions within the same component.
   *
   * @type {string | undefined}
   * @memberOf CrudFormComponent
   */
  @Input()
  action?: string;

  /**
   * @description The current CRUD operation being performed.
   * @summary Specifies the type of operation this form is handling (CREATE, READ, UPDATE, DELETE).
   * This is a required input that determines form behavior, validation rules, and available actions.
   * The operation affects form state, button visibility, and submission logic.
   *
   * @type {CrudOperations}
   * @required
   * @memberOf CrudFormComponent
   */
  @Input({ required: true })
  operation!: CrudOperations;

  /**
   * @description Custom event handlers for form actions.
   * @summary A record of event handler functions keyed by event names that can be
   * triggered during form operations. These handlers provide extensibility for
   * custom business logic and can be invoked for various form events and actions.
   *
   * @type {HandlerLike}
   * @memberOf CrudFormComponent
   */
  @Input()
  handlers!: HandlerLike;

  /**
   * @description Angular reactive FormGroup for form state management.
   * @summary The FormGroup instance that manages all form controls, validation,
   * and form state. This is the main interface for accessing form values and
   * controlling form behavior. May be undefined for read-only operations.
   *
   * @type {FormGroup | undefined}
   * @memberOf CrudFormComponent
   */
  @Input()
  formGroup!: FormGroup | undefined;

  /**
   * @description Path to the parent FormGroup, if nested.
   * @summary Full dot-delimited path of the parent FormGroup. Set only when is part of a nested structure.
   *
   * @type {string}
   * @memberOf CrudFormComponent
   */
  @Input()
  childOf?: string;

  /**
   * @description Unique identifier for the form renderer.
   * @summary A unique string identifier used to register and manage this form
   * instance within the NgxFormService. This ID is also used as the HTML id
   * attribute for the form element, enabling DOM queries and form management.
   *
   * @type {string}
   * @memberOf CrudFormComponent
   */
  @Input()
  rendererId!: string;

  /**
   * @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}
   * @memberOf CrudFormComponent
   */
  @Input()
  uid: string = generateRandomValue(12);


  /**
   * @description Event emitter for form submission events.
   * @summary Emits CrudFormEvent objects when the form is submitted, providing
   * form data, component information, and any associated handlers to parent
   * components. This enables decoupled handling of form submission logic.
   *
   * @type {EventEmitter<CrudFormEvent>}
   * @memberOf CrudFormComponent
   */
  @Output()
  submitEvent: EventEmitter<CrudFormEvent> = new EventEmitter<CrudFormEvent>();

  /**
   * @description Logger instance for the component.
   * @summary Provides logging capabilities for the component, allowing for consistent
   * and structured logging of information, warnings, and errors. This logger is initialized
   * in the ngOnInit method using the getLogger function from the ForAngularModule.
   *
   * The logger is used throughout the component to record important events, debug information,
   * and potential issues. It helps in monitoring the component's behavior, tracking the flow
   * of operations, and facilitating easier debugging and maintenance.
   *
   * @type {Logger}
   * @private
   * @memberOf CrudFormComponent
   */
  private logger!: Logger;

  /**
   * @description Angular Location service.
   * @summary Injected service that provides access to the browser's URL and history.
   * This service is used for interacting with the browser's history API, allowing
   * for back navigation and URL manipulation outside of Angular's router.
   *
   * @private
   * @type {Location}
   * @memberOf CrudFormComponent
   */
  private location: Location = inject(Location);

  // ngAfterViewInit() {
    // if (![OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
    //   NgxFormService.formAfterViewInit(this, this.rendererId);
  // }

  /**
   * @description Component initialization lifecycle method.
   * @summary Initializes the component by setting up the logger, configuring form state
   * based on the operation type, and merging configuration options. For READ and DELETE
   * operations, the formGroup is set to undefined since these operations don't require
   * form input. Configuration options are merged with default settings.
   *
   * @returns {Promise<void>}
   * @memberOf CrudFormComponent
   */
  async ngOnInit() {
    if (!this.logger)
      this.logger = getLogger(this);
    if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
      this.formGroup = undefined;
    this.options = Object.assign(
      {},
      DefaultFormReactiveOptions,
      this.options || {},
    );

  }

  /**
   * @description Component cleanup lifecycle method.
   * @summary Performs cleanup operations when the component is destroyed.
   * Unregisters the FormGroup from the NgxFormService to prevent memory leaks
   * and ensure proper resource cleanup.
   *
   * @returns {void}
   * @memberOf CrudFormComponent
   */
  ngOnDestroy() {
    if (this.formGroup)
      NgxFormService.unregister(this.formGroup);
  }

  /**
   * @description Handles form submission with validation and event emission.
   * @summary Processes form submission by first preventing default browser behavior,
   * then validating all form fields using NgxFormService. If validation passes,
   * extracts form data and emits a submitEvent with the data, component information,
   * and any associated handlers. Returns false if validation fails.
   *
   * @param {SubmitEvent} event - The browser's native form submit event
   * @returns {Promise<boolean | void>} Returns false if validation fails, void if successful
   * @memberOf CrudFormComponent
   */
  async submit(event: SubmitEvent): Promise<boolean | void> {
    event.preventDefault();
    event.stopImmediatePropagation();
    if (!NgxFormService.validateFields(this.formGroup as FormGroup))
      return false;
    const data = NgxFormService.getFormData(this.formGroup as FormGroup);
    this.submitEvent.emit({
      data,
      component: 'CrudFormComponent',
      name: this.action || EventConstants.SUBMIT,
      handlers: this.handlers,
    });
  }

  /**
   * @description Handles form reset or navigation back functionality.
   * @summary Provides different reset behavior based on the current operation.
   * For CREATE and UPDATE operations, resets the form to its initial state.
   * For READ and DELETE operations, navigates back in the browser history
   * since these operations don't have modifiable form data to reset.
   *
   * @returns {void}
   * @memberOf CrudFormComponent
   */
  handleReset(): void {
    if(![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation))
      return NgxFormService.reset(this.formGroup as FormGroup);
    this.location.back();
  }

  /**
   * @description Handles delete operations by emitting delete events.
   * @summary Processes delete requests by emitting a submit event with the
   * record's unique identifier as data. This allows parent components to
   * handle the actual deletion logic while maintaining separation of concerns.
   * The event includes the uid and standard component identification.
   *
   * @returns {void}
   * @memberOf CrudFormComponent
   */
  handleDelete(): void {
    this.submitEvent.emit({
      data: this.uid,
      component: 'CrudFormComponent',
      name: EventConstants.SUBMIT,
    });
  }

  /**
   * @description Reference to CRUD operation constants for template usage.
   * @summary Exposes the OperationKeys enum to the component template, enabling
   * conditional rendering and behavior based on operation types. This protected
   * readonly property ensures that template logic can access operation constants
   * while maintaining encapsulation and preventing accidental modification.
   *
   * @type {CrudOperations}
   * @protected
   * @readonly
   * @memberOf CrudFormComponent
   */
  protected readonly OperationKeys = OperationKeys;
}