material-dynamic-forms
Version:
¡Crea formularios dinámicos, potentes y configurables en Angular usando Material Design! 🚀
838 lines (833 loc) • 86.6 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Component, Inject, Input, Output, NgModule } from '@angular/core';
import * as i4 from '@angular/forms';
import { Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
import * as i2 from '@angular/common/http';
import { HttpParams } from '@angular/common/http';
import { ConfirmEventType } from 'primeng/api';
import { throwError } from 'rxjs';
import * as i1 from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import * as i10 from '@angular/material/core';
import { NativeDateAdapter, DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatNativeDateModule, MatRippleModule } from '@angular/material/core';
import * as i3 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i5 from '@angular/material/checkbox';
import { MatCheckboxModule } from '@angular/material/checkbox';
import * as i1$1 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i7 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i8 from '@angular/material/form-field';
import { MatFormFieldModule } from '@angular/material/form-field';
import * as i9 from '@angular/material/select';
import { MatSelectModule } from '@angular/material/select';
import * as i11 from '@angular/material/button-toggle';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import * as i12 from '@angular/material/input';
import { MatInputModule } from '@angular/material/input';
import * as i13 from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
import * as i14 from '@angular/material/datepicker';
import { MatDatepickerModule } from '@angular/material/datepicker';
import * as i15 from 'primeng/multiselect';
import { MultiSelectModule } from 'primeng/multiselect';
import * as i16 from 'primeng/dropdown';
import { DropdownModule } from 'primeng/dropdown';
import * as i17 from 'primeng/autocomplete';
import { AutoCompleteModule } from 'primeng/autocomplete';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatCardModule } from '@angular/material/card';
import { MatListModule } from '@angular/material/list';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatBadgeModule } from '@angular/material/badge';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatRadioModule } from '@angular/material/radio';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatTabsModule } from '@angular/material/tabs';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatChipsModule } from '@angular/material/chips';
import * as i2$1 from '@angular/platform-browser';
var eDataType;
(function (eDataType) {
eDataType[eDataType["input"] = 0] = "input";
eDataType[eDataType["select"] = 1] = "select";
eDataType[eDataType["date"] = 2] = "date";
eDataType[eDataType["select_search"] = 3] = "select_search";
eDataType[eDataType["range"] = 4] = "range";
eDataType[eDataType["button"] = 5] = "button";
eDataType[eDataType["checkbox"] = 6] = "checkbox";
eDataType[eDataType["dateMultiple"] = 7] = "dateMultiple";
eDataType[eDataType["tex_area"] = 8] = "tex_area";
eDataType[eDataType["button_toggle"] = 9] = "button_toggle";
eDataType[eDataType["select_chips"] = 10] = "select_chips";
eDataType[eDataType["select_autocomplete"] = 11] = "select_autocomplete";
eDataType[eDataType["input_autocomplete"] = 12] = "input_autocomplete";
})(eDataType || (eDataType = {}));
var eControlType;
(function (eControlType) {
eControlType[eControlType["button"] = 0] = "button";
eControlType[eControlType["checkbox"] = 1] = "checkbox";
eControlType[eControlType["date"] = 2] = "date";
eControlType[eControlType["email"] = 3] = "email";
eControlType[eControlType["number"] = 4] = "number";
eControlType[eControlType["tel"] = 5] = "tel";
eControlType[eControlType["text"] = 6] = "text";
})(eControlType || (eControlType = {}));
var eTypeMOdel;
(function (eTypeMOdel) {
eTypeMOdel[eTypeMOdel["boolean"] = 0] = "boolean";
eTypeMOdel[eTypeMOdel["number"] = 1] = "number";
})(eTypeMOdel || (eTypeMOdel = {}));
function handleError(error) {
let errorData = {
status: 0,
message: '',
};
switch (error.status) {
case 401 /* HttpStatusCode.Unauthorized */:
removeItemStorage();
return throwError(error.error.message);
case 400 /* HttpStatusCode.BadRequest */:
case 409 /* HttpStatusCode.Conflict */:
return throwError(error.error.message);
case 402 /* HttpStatusCode.PaymentRequired */:
errorData.status = error.status;
errorData.message = error.error.data.mensaje;
return throwError(errorData);
case 422 /* HttpStatusCode.UnprocessableEntity */:
return throwError(error.error);
case 425 /* HttpStatusCode.TooEarly */:
return throwError(error.error.message.errors);
case 500 /* HttpStatusCode.InternalServerError */:
return throwError(error.error.previous.message);
default:
return throwError('Something bad happened; please try again later.');
}
}
function removeNullValuesFromQueryParams(params) {
const paramsKeysAux = params.keys();
paramsKeysAux.forEach((key) => {
const value = params.get(key);
if (value === null ||
value === undefined ||
value === '' ||
value === 'undefined') {
params['map'].delete(key);
}
});
return params;
}
const FunctionRejectData = (type, _messageService) => {
switch (type) {
case ConfirmEventType.REJECT:
_messageService.add({
severity: 'error',
summary: 'Rechazada',
detail: 'Rechazada la acción',
});
break;
case ConfirmEventType.CANCEL:
_messageService.add({
severity: 'warn',
summary: 'Cancelado',
detail: 'Cancelada la acción',
});
break;
}
};
function filterObjectKeys(obj, keysToKeep) {
const newObj = {};
keysToKeep.forEach((key) => {
if (key in obj) {
newObj[key] = obj[key];
}
});
return newObj;
}
function getValueFromProperty(row, property) {
if (!row || !property) {
return undefined;
}
const properties = property.split('.');
// Itera sobre las propiedades para acceder al valor final
let value = row;
for (const prop of properties) {
value = value[prop];
if (value === undefined) {
return undefined;
}
}
return value;
}
function removeItemStorage() {
localStorage.removeItem('token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('session_id');
localStorage.removeItem('remember');
}
const transformToObject = (objectSelect) => {
return {
id: Number(objectSelect.key),
nombre: objectSelect.value,
};
};
class DynamicFormService {
constructor(fb, http) {
this.fb = fb;
this.http = http;
this.baseUrl = '';
// Agregamos esta variable para almacenar los valores externos
this.externalDependenciesValues = {};
// Declarar formGroupControls como propiedad
this.formGroupControls = {};
// Variable para manejar el modo de edición
this.isEditMode = false;
this.isReadonly = false; // Add readonly flag
this.rangeValidator = (group) => {
const desde = Number(group.get('desde')?.value);
const hasta = Number(group.get('hasta')?.value);
return desde <= hasta ? null : { rangeError: true };
};
}
setBaseUrl(url) {
this.baseUrl = url;
}
// Método para establecer el modo de edición
setEditMode(isEdit) {
this.isEditMode = isEdit;
}
// Add method to set readonly
setReadonly(isReadonly) {
this.isReadonly = isReadonly;
}
// Permite setear/actualizar los valores externos desde el componente
setExternalDependenciesValues(values) {
this.externalDependenciesValues = {
...this.externalDependenciesValues,
...values,
};
}
removeExternalDependency(key) {
if (this.externalDependenciesValues.hasOwnProperty(key)) {
delete this.externalDependenciesValues[key];
}
}
functionInitComponent(fieldGroupsDynamicForm, form) {
this.formGroupControls = {};
let isRangeValidator = false;
const initializeControl = (field) => {
const initialValue = field.value !== undefined
? field.value
: field.type === eDataType.select_chips
? []
: '';
const validators = this.isReadonly ? [] : field.validation || [];
const isDisabled = this.isReadonly || !!field.dependency || field.disabled;
return this.fb.control({ value: initialValue, disabled: isDisabled }, validators);
};
const handleDependenciesValue = (field, control) => {
if (!field.valueDependency && !field.valueExcludeDependency) {
return;
}
const updateField = () => {
const depValue = field.valueDependency
? this.formGroupControls[field.valueDependency.dependency] &&
this.formGroupControls[field.valueDependency.dependency].value
: field.valueExcludeDependency
? this.formGroupControls[field.valueExcludeDependency.dependency] &&
this.formGroupControls[field.valueExcludeDependency.dependency]
.value
: undefined;
const valueComparar = field.valueDependency
? field.valueDependency.valueDep
: field.valueExcludeDependency
? field.valueExcludeDependency.valueDep
: undefined;
if (field.valueDependency && depValue === valueComparar) {
control.enable();
control.clearValidators();
control.setValidators(field.valueDependency.validation ?? []);
control.updateValueAndValidity();
}
else if (field.valueExcludeDependency && depValue !== valueComparar) {
control.enable();
control.clearValidators();
control.setValidators(field.valueExcludeDependency.validation ?? []);
control.updateValueAndValidity();
}
else {
control.disable();
control.reset();
control.clearValidators();
control.updateValueAndValidity();
}
};
if (field.valueDependency) {
this.formGroupControls[field.valueDependency.dependency].valueChanges.subscribe(updateField);
}
else if (field.valueExcludeDependency) {
this.formGroupControls[field.valueExcludeDependency.dependency].valueChanges.subscribe(updateField);
}
updateField();
};
const handleDependencies = (field, control) => {
if (!field.dependency) {
return;
}
const dependencyFields = field.dependency.split(',');
dependencyFields.forEach((depField) => {
if (!this.formGroupControls[depField]) {
this.formGroupControls[depField] = this.fb.control('', Validators.required);
}
});
const updateField = () => {
if ((field.valueDependency || field.valueExcludeDependency) &&
control.disabled) {
return;
}
const allDependenciesFilled = dependencyFields.every((depField) => this.formGroupControls[depField] &&
this.formGroupControls[depField]?.value);
if (allDependenciesFilled) {
const dependencyValue = this.formGroupControls[dependencyFields[0]].value;
if (!field.valueDependency &&
!field.valueExcludeDependency &&
field.disabled) {
control.disable();
}
else {
control.enable();
}
if (!this.isEditMode)
control.reset();
// Verificar si hay dependencias externas
const hasExternalDeps = field.externalDependencies && field.externalDependencies.length > 0;
// Aquí se unifica la lógica de obtención de datos
if (field.apiUrl && dependencyValue) {
this.fetchOptions(field, dependencyValue, hasExternalDeps);
}
}
else {
control.reset();
field.options = [];
if (!field.valueDependency && !field.valueExcludeDependency) {
control.disable();
}
}
};
dependencyFields.forEach((depField) => {
this.formGroupControls[depField].valueChanges.subscribe(updateField);
});
updateField();
};
const handleDynamicDateConstraints = (field, control) => {
if (!field.dateDependency || (!field.minDateFn && !field.maxDateFn)) {
return;
}
const dependencyField = this.formGroupControls[field.dateDependency];
if (!dependencyField) {
console.error(`El campo de dependencia ${field.dateDependency} no existe.`);
return;
}
const updateConstraints = () => {
const dependencyValue = dependencyField.value
? new Date(dependencyField.value)
: null;
if (dependencyValue) {
// Asegurarse de que la hora sea 00:00:00
dependencyValue.setHours(0, 0, 0, 0);
}
if (field.minDateFn && dependencyValue) {
const calculatedMinDate = field.minDateFn(dependencyValue);
field.minDate = calculatedMinDate;
control.setValidators([
...(control.validator ? [control.validator] : []),
Validators.min(calculatedMinDate.getTime()),
]);
}
if (field.maxDateFn && dependencyValue) {
const calculatedMaxDate = field.maxDateFn(dependencyValue);
field.maxDate = calculatedMaxDate;
control.setValidators([
...(control.validator ? [control.validator] : []),
Validators.max(calculatedMaxDate.getTime()),
]);
}
control.updateValueAndValidity();
};
dependencyField.valueChanges.subscribe(updateConstraints);
updateConstraints(); // Aplicar al inicializar
// Forzar una actualización manual del valor del campo dependiente
setTimeout(() => {
dependencyField.updateValueAndValidity();
}, 0);
};
fieldGroupsDynamicForm.forEach((group) => {
group.fields.forEach((field) => {
if (field.visible) {
// Inicializar control
const control = initializeControl(field);
// Agregar control al objeto de controles
if (field.name)
this.formGroupControls[field.name] = control;
// Agregar opciones al campo select sin dependencias
if (field.apiUrl &&
!field.urlParamDependency &&
!field.urlQueryParam &&
!field.urlEndParam) {
this.fetchOptions(field, null, false);
}
if (!this.isReadonly) {
// Manejar dependencias de values de campos internas
if (field.valueDependency || field.valueExcludeDependency)
handleDependenciesValue(field, control);
// Manejar dependencias de campos
if (field.dependency)
handleDependencies(field, control);
// Manejar dependencias de fechas
if (field.type === eDataType.date)
handleDynamicDateConstraints(field, control);
}
}
});
isRangeValidator = group.isRangeValidator ?? false;
});
form = this.fb.group(this.formGroupControls);
if (isRangeValidator)
form.setValidators(this.rangeValidator);
if (this.isEditMode) {
this.markControlsAsTouched(form);
}
if (this.isReadonly) {
form.disable(); // Disable the entire form if readonly
}
return form;
}
markControlsAsTouched(form) {
Object.keys(form.controls).forEach((key) => {
const control = form.get(key);
if (control) {
control.markAsTouched();
control.updateValueAndValidity(); // Actualiza los errores y validaciones
}
});
}
handleExternalDependenciesService(field, control) {
if (!control) {
console.warn(`Control no encontrado para el campo ${field.name}`);
return () => { };
}
const checkDependencies = () => {
// 1) Verificar dependencias externas
const depsExtOk = field.externalDependencies?.every((dep) => this.externalDependenciesValues?.[dep] !== undefined);
// 2) Verificar dependencias de formulario (field.dependency)
let depsFormOk = true;
if (field.dependency) {
const dependencyFields = field.dependency.split(',');
// Chequea que todos esos campos tengan algún valor
depsFormOk = dependencyFields.every((depField) => !!this.formGroupControls[depField]?.value);
}
// Si todo está OK, habilitar campo y llamar a la API
if (depsExtOk && depsFormOk) {
control.enable();
if (field.apiUrl) {
// Verificar si hay dependencias externas
const hasExternalDeps = field.externalDependencies && field.externalDependencies.length > 0;
// fetchOptions con "useExternalDeps = true" si hay dependencias externas
this.fetchOptions(field, undefined, hasExternalDeps);
}
}
else if (!this.isEditMode || !control.value) {
// Si no está en modo edición o el control no tiene valor, deshabilita
control.disable();
control.reset();
field.options = [];
}
// En modo edición y tiene valor, no hacer nada para mantener el valor
};
// Ejecuta inicialmente
checkDependencies();
// Retorna la función para que el componente la invoque cuando cambien las dependencias
return checkDependencies;
}
functionGetFormValue(form, fieldGroupsDynamicForm) {
let dataFilterSend = {};
fieldGroupsDynamicForm.forEach((group) => {
group.fields.forEach((fieldDynamicForm) => {
const control = form.get(fieldDynamicForm.name);
if (control) {
if (control.value !== '' && control.value !== null) {
fieldDynamicForm.active = true;
switch (fieldDynamicForm.type) {
case eDataType.select:
fieldDynamicForm.value = control.value;
if (control.value === -1) {
fieldDynamicForm.options = [
{ key: -1, value: 'TODOS' },
...fieldDynamicForm.options,
];
}
fieldDynamicForm.selectedOption =
fieldDynamicForm.options?.find((option) => option.key === control.value);
fieldDynamicForm.filterParam
? (dataFilterSend[fieldDynamicForm.filterParam] =
fieldDynamicForm.selectedOption
? fieldDynamicForm.selectedOption['key']
: null)
: (dataFilterSend[fieldDynamicForm.name] =
fieldDynamicForm.selectedOption
? transformToObject(fieldDynamicForm.selectedOption)
: null);
break;
case eDataType.date:
fieldDynamicForm.value = control.value || null;
dataFilterSend[fieldDynamicForm.name] = fieldDynamicForm.value;
break;
default:
fieldDynamicForm.value =
fieldDynamicForm.typeModel === eTypeMOdel.number
? Number(control.value)
: control.value;
//fieldDynamicForm.value = control.value;
dataFilterSend[fieldDynamicForm.name] = fieldDynamicForm.value;
break;
}
}
else {
fieldDynamicForm.active = false;
fieldDynamicForm.value = null;
dataFilterSend[fieldDynamicForm.name] = null;
}
}
});
});
return dataFilterSend;
}
buildUrlAndParams(field, dependencyValue, useExternalDeps = false) {
let url = `${this.baseUrl}${field.apiUrl}`;
let params = new HttpParams();
// 1. urlParamDependency (ej: /api/{id})
if (field.urlParamDependency && dependencyValue) {
url = url.replace(/\{.*?\}/g, dependencyValue);
}
// 2. urlQueryParam (ej: ?depField=value)
else if (field.urlQueryParam && field.dependency) {
const dependencyFields = field.dependency.split(',');
dependencyFields.forEach((depField) => {
params = params.set(depField, this.formGroupControls[depField].value);
});
}
// 3. urlEndParam (ej: /api/id)
else if (field.urlEndParam && dependencyValue) {
url = `${url}/${dependencyValue}`;
}
// 4. Agregar dependencias externas si se indica
if (useExternalDeps && field.externalDependencies) {
field.externalDependencies.forEach((dep) => {
if (this.externalDependenciesValues[dep] !== undefined) {
params = params.append(dep, this.externalDependenciesValues[dep]);
}
});
}
return { url, params };
}
fetchOptions(field, dependencyValue, useExternalDeps = false) {
if (!field.apiUrl) {
return;
}
const { url, params } = this.buildUrlAndParams(field, dependencyValue, useExternalDeps);
this.http.get(url, { params }).subscribe((res) => {
field.options = field.subscribeMap
? field.subscribeMap(res).map((item) => ({
key: item.id,
value: field.valueTOShow
? getValueFromProperty(item, field.valueTOShow)
: item.nombre || item.name,
data: item.data,
}))
: res.data.map((item) => ({
key: item.id,
value: field.valueTOShow
? item[field.valueTOShow]
: item.nombre || item.name,
data: item,
}));
field.options?.sort((a, b) => (a.value || '').localeCompare(b.value || ''));
});
}
}
DynamicFormService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DynamicFormService, deps: [{ token: i4.FormBuilder }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
DynamicFormService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DynamicFormService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DynamicFormService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i4.FormBuilder }, { type: i2.HttpClient }]; } });
const CONST_TYPE_STRING = 'string';
const CONST_TYPE_NUMBER = 'number';
const CONST_TYPE_NUMERIC = 'numeric';
const CONST_TYPE_LONG = 'long';
const CONST_DISPLAY_FORMAT_INPUT = 'input';
const CONST_DISPLAY_FORMAT_INPUTMONTH = 'inputMonth';
class AppDateAdapter extends NativeDateAdapter {
parse(value) {
if (typeof value === CONST_TYPE_STRING && value.indexOf('/') > -1) {
const str = value.split('/');
const year = Number(str[2]);
const month = Number(str[1]) - 1;
const date = Number(str[0]);
return new Date(year, month, date);
}
const timestamp = typeof value === CONST_TYPE_NUMBER ? value : Date.parse(value);
return isNaN(timestamp) ? null : new Date(timestamp);
}
format(date, displayFormat) {
if (displayFormat === CONST_DISPLAY_FORMAT_INPUT) {
let day = date.getDate();
let month = date.getMonth() + 1;
let year = date.getFullYear();
return this._to2digit(day) + '/' + this._to2digit(month) + '/' + year;
}
else if (displayFormat === CONST_DISPLAY_FORMAT_INPUTMONTH) {
let month = date.getMonth() + 1;
let year = date.getFullYear();
return this._to2digit(month) + '/' + year;
}
else {
return date.toDateString();
}
}
_to2digit(n) {
return ('00' + n).slice(-2);
}
}
const APP_DATE_FORMATS = {
parse: {
dateInput: { month: 'short', year: CONST_TYPE_NUMERIC, day: CONST_TYPE_NUMERIC },
},
display: {
dateInput: CONST_DISPLAY_FORMAT_INPUT,
monthYearLabel: CONST_DISPLAY_FORMAT_INPUTMONTH,
dateA11yLabel: { year: CONST_TYPE_NUMERIC, month: CONST_TYPE_LONG, day: CONST_TYPE_NUMERIC },
monthYearA11yLabel: { year: CONST_TYPE_NUMERIC, month: CONST_TYPE_LONG },
},
};
class DynamicFormComponent {
constructor(data, modal, dynamicFormService) {
this.data = data;
this.modal = modal;
this.dynamicFormService = dynamicFormService;
this.isModal = false;
this.isEdit = false;
this.isReadonlyForm = false;
this.changeValueSelected = new EventEmitter();
this.formValidityChange = new EventEmitter();
this.externalDependenciesValuesChanged = new EventEmitter();
this.eDataType = eDataType;
this.title = '';
this.titleButton = 'Guardar';
this.selectedOption = null;
}
ngOnInit() {
this.inizializarForm();
}
ngOnDestroy() {
this.form.reset();
}
ngOnChanges(changes) {
// Detecta si el valor de resetFormTrigger cambia
if (changes['resetFormTrigger'] &&
changes['resetFormTrigger'].currentValue) {
this.form.reset(); // Resetea el formulario
}
// Detecta si el valor de baseUrl cambia
if (changes['baseUrl'] &&
changes['baseUrl'].currentValue) {
this.dynamicFormService.setBaseUrl(this.baseUrl);
}
// Detecta si el valor de isEdit cambia
if (changes['isEdit'] &&
changes['isEdit'].currentValue) {
this.dynamicFormService.setEditMode(this.isEdit);
}
}
inizializarForm() {
if (this.isReadonlyForm) {
this.dynamicFormService.setReadonly(true);
}
// En el caso que se esta utilizando un formulario desde una modal MatDialog
if (this.data?.dataForm) {
this.fieldGroups = this.data.dataForm.fieldGroups;
this.isModal = true;
this.title = this.data.title;
this.titleButton = this.data.dataForm.titleButtonAcceptForm;
if (this.data.dataForm.baseUrl) {
this.dynamicFormService.setBaseUrl(this.data.dataForm.baseUrl);
this.baseUrl = this.data.dataForm.baseUrl;
}
if (this.data.dataForm.form) {
this.form = this.data.dataForm.form;
}
if (this.data.dataForm.edit) {
this.isEdit = this.data.dataForm.edit;
this.dynamicFormService.setEditMode(this.data.dataForm.edit);
}
}
// Inicializa el formulario
if (this.fieldGroups) {
this.form = this.dynamicFormService.functionInitComponent(this.fieldGroups, this.form);
// Manejar campos con dependencias externas
this.fieldGroups.forEach((group) => {
group.fields.forEach((field) => {
if (field.externalDependencies) {
this.handleExternalDependencies(field);
}
});
});
}
// Emito la validacion actual del form
this.formValidityChange.emit(this.form.valid);
// Escucha los cambios de estado del formulario y emite el resultado
this.form.statusChanges.subscribe(() => {
this.formValidityChange.emit(this.form.valid);
});
}
/**
* Método para manejar dependencias externas.
*/
handleExternalDependencies(field) {
const control = this.form.get(field.name);
// Llamamos al servicio
const checkDependencies = this.dynamicFormService.handleExternalDependenciesService(field, control);
// Suscribimos el EventEmitter para volver a verificar las dependencias
// cuando cambien los valores externos
this.externalDependenciesValuesChanged.subscribe(() => {
checkDependencies?.();
});
}
// Método para actualizar las dependencias externas
updateExternalDependencies(values) {
// Actualizas la variable en el servicio
this.dynamicFormService.setExternalDependenciesValues(values);
// Emite el evento si deseas que otros sitios en el componente reaccionen
this.externalDependenciesValuesChanged.emit();
}
// Método para actualizar el valor de un campo del formulario
updateFormField(fieldName, value) {
if (this.form && this.form.controls[fieldName]) {
this.form.controls[fieldName].setValue(value);
}
}
/**
* Función pública para deshabilitar un campo específico
* @param fieldName Nombre del campo a deshabilitar
*/
disableField(fieldName) {
const control = this.form.get(fieldName);
if (control) {
control.disable();
}
else {
console.warn(`El campo con nombre "${fieldName}" no existe en el formulario.`);
}
}
/**
* Función pública para habilitar un campo específico
* @param fieldName Nombre del campo a habilitar
*/
enableField(fieldName) {
const control = this.form.get(fieldName);
if (control) {
control.enable();
}
else {
console.warn(`El campo con nombre "${fieldName}" no existe en el formulario.`);
}
}
removeExternalDependency(key) {
this.dynamicFormService.removeExternalDependency(key);
this.externalDependenciesValuesChanged.emit();
}
refreshDependentFields() {
// Emit the event to refresh dependencies
this.externalDependenciesValuesChanged.emit();
}
simulateFieldChange(fieldName) {
const control = this.form.get(fieldName);
if (control) {
const currentValue = control.value;
control.setValue(currentValue);
}
}
getFormFieldValue(fieldName) {
return this.form.get(fieldName)?.value;
}
onSubmit() {
let listadoFormGet = this.dynamicFormService.functionGetFormValue(this.form, this.fieldGroups);
this.modal.close(listadoFormGet);
}
emitEvent(nameForm, event) {
let value;
value = this.form.get(nameForm.name)?.value;
if (event) {
value = event.value;
}
this.changeValueSelected.emit({ form: nameForm, value: value });
}
getForm() {
return this.form;
}
getErrorMessage(fieldName) {
const control = this.form.get(fieldName);
if (control?.hasError('required')) {
return 'Este campo es obligatorio';
}
if (control?.hasError('min')) {
return `El valor debe ser mayor o igual a ${control.getError('min').min}`;
}
if (control?.hasError('max')) {
return `El valor debe ser menor o igual a ${control.getError('max').max}`;
}
if (control?.hasError('email')) {
return 'Debe ser un correo electrónico válido';
}
if (control?.hasError('pattern')) {
return 'El formato no es válido';
}
if (control?.hasError('minlength')) {
return `Debe tener al menos ${control.getError('minlength').requiredLength} caracteres`;
}
if (control?.hasError('maxlength')) {
return `Debe tener como máximo ${control.getError('maxlength').requiredLength} caracteres`;
}
// Agrega más validaciones según sea necesario
return null;
}
getGridColumns(columnCount) {
return `repeat(${columnCount || 1}, 1fr)`;
}
getColSpanClass(colspan) {
return colspan ? `col-span-${colspan}` : 'col-span-1';
}
}
DynamicFormComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DynamicFormComponent, deps: [{ token: MAT_DIALOG_DATA }, { token: i1.MatDialogRef }, { token: DynamicFormService }], target: i0.ɵɵFactoryTarget.Component });
DynamicFormComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: DynamicFormComponent, selector: "lib-dynamic-form", inputs: { fieldGroups: "fieldGroups", isModal: "isModal", baseUrl: "baseUrl", resetFormTrigger: "resetFormTrigger", isEdit: "isEdit", isReadonlyForm: "isReadonlyForm" }, outputs: { changeValueSelected: "changeValueSelected", formValidityChange: "formValidityChange", externalDependenciesValuesChanged: "externalDependenciesValuesChanged" }, providers: [
DynamicFormService,
{
provide: DateAdapter,
useClass: AppDateAdapter,
},
{
provide: MAT_DATE_FORMATS,
useValue: APP_DATE_FORMATS,
},
{
provide: MAT_DATE_LOCALE,
useValue: 'es-ES',
},
], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"my-2\" (ngSubmit)=\"onSubmit()\">\r\n <div class=\"flex items-center\" mat-dialog-title *ngIf=\"isModal\">\r\n <h2 class=\"headline m-0 flex-auto\">{{ title }}</h2>\r\n </div>\r\n <mat-divider *ngIf=\"isModal\" class=\"-mx-6 text-border\"></mat-divider>\r\n\r\n <mat-dialog-content class=\"flex flex-col\">\r\n <div *ngFor=\"let groups of fieldGroups\">\r\n <div mat-dialog-title *ngIf=\"groups.title\">\r\n <h6 class=\"title-group m-0 flex-auto\">{{ groups.title }}</h6>\r\n </div>\r\n <div\r\n class=\"grid-container\"\r\n [style.gridTemplateColumns]=\"getGridColumns(groups.countColumns || 1)\"\r\n >\r\n <div\r\n *ngFor=\"let field of groups.fields\"\r\n [class]=\"getColSpanClass(field.colspan)\"\r\n >\r\n <div [ngSwitch]=\"field.type\" *ngIf=\"field.visible\">\r\n <!-- INPUT -->\r\n <mat-form-field\r\n *ngSwitchCase=\"eDataType.input\"\r\n appearance=\"outline\"\r\n class=\"w-full\"\r\n [attr.style]=\"\r\n field.background\r\n ? '--dynamic-background-color: ' + field.background\r\n : 'transparent'\r\n \"\r\n >\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input\r\n [type]=\"field.controlType!\"\r\n matInput\r\n [placeholder]=\"field.placeholder!\"\r\n [formControlName]=\"field.name!\"\r\n [value]=\"field.value ? field.value : ''\"\r\n (blur)=\"emitEvent(field)\"\r\n />\r\n <mat-icon\r\n [svgIcon]=\"field.icon!\"\r\n class=\"icon-sm\"\r\n matSuffix\r\n ></mat-icon>\r\n\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </mat-form-field>\r\n\r\n <!-- SELECT -->\r\n <mat-form-field\r\n *ngSwitchCase=\"eDataType.select\"\r\n appearance=\"outline\"\r\n class=\"w-full\"\r\n [attr.style]=\"\r\n field.background\r\n ? '--dynamic-background-color: ' + field.background\r\n : 'transparent'\r\n \"\r\n >\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-select\r\n [formControlName]=\"field.name!\"\r\n (selectionChange)=\"emitEvent(field, $event)\"\r\n >\r\n <mat-option *ngIf=\"field.allOption\" [value]=\"-1\"\r\n >TODOS</mat-option\r\n >\r\n <mat-option\r\n *ngFor=\"let option of field.options\"\r\n [value]=\"option.key\"\r\n >\r\n {{ option.value }}\r\n </mat-option>\r\n </mat-select>\r\n\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </mat-form-field>\r\n\r\n <!-- SELECT WITH AUTOCOMPLETE -->\r\n <div\r\n *ngSwitchCase=\"eDataType.select_autocomplete\"\r\n style=\"margin-bottom: 15px\"\r\n >\r\n <h2 *ngIf=\"field.label\" class=\"label-custom\">\r\n {{ field.label }}\r\n </h2>\r\n <p-dropdown\r\n [options]=\"field.options!\"\r\n [formControlName]=\"field.name\"\r\n [filter]=\"true\"\r\n [showClear]=\"true\"\r\n [placeholder]=\"field.placeholder || 'Seleccione'\"\r\n (onChange)=\"emitEvent(field, $event)\"\r\n optionLabel=\"value\"\r\n optionValue=\"key\"\r\n appendTo=\"body\"\r\n filterBy=\"value\"\r\n >\r\n </p-dropdown>\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </div>\r\n\r\n <!-- INPUT AUTOCOMPLETE -->\r\n <div\r\n *ngSwitchCase=\"eDataType.input_autocomplete\"\r\n style=\"margin-bottom: 15px\"\r\n >\r\n <h2 *ngIf=\"field.label\" class=\"label-custom\">\r\n {{ field.label }}\r\n </h2>\r\n <span class=\"p-input-icon-right w-full mr-2\">\r\n <i class=\"pi pi-search\"></i>\r\n <p-autoComplete\r\n [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [suggestions]=\"field.inputAutoComplete?.suggestions || []\"\r\n [showClear]=\"field.inputAutoComplete?.showClear || true\"\r\n (completeMethod)=\"\r\n field.inputAutoComplete?.onCompleteMethod($event)\r\n \"\r\n (onClear)=\"field.inputAutoComplete?.onClear($event)\"\r\n (onSelect)=\"field.inputAutoComplete?.onSelect($event)\"\r\n field=\"value\"\r\n optionValue=\"key\"\r\n styleClass=\"w-full\"\r\n inputStyleClass=\"w-full p-inputtext\"\r\n appendTo=\"body\"\r\n >\r\n </p-autoComplete>\r\n </span>\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </div>\r\n\r\n <!-- SELECT-CHIPS -->\r\n <div *ngSwitchCase=\"eDataType.select_chips\">\r\n <h2 class=\"label-custom\">{{ field.label }}</h2>\r\n <p-multiSelect\r\n [options]=\"field.options!\"\r\n [formControlName]=\"field.name\"\r\n [defaultLabel]=\"field.placeholder || ''\"\r\n (onChange)=\"emitEvent(field, $event)\"\r\n optionLabel=\"value\"\r\n optionValue=\"key\"\r\n display=\"chip\"\r\n appendTo=\"body\"\r\n ></p-multiSelect>\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </div>\r\n\r\n <!-- DATE -->\r\n <ng-container *ngSwitchCase=\"eDataType.date\">\r\n <ng-container\r\n *ngIf=\"!field.multiple; then singleDate; else rangeDate\"\r\n >\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- DATE-SINGLE -->\r\n <ng-template #singleDate>\r\n <mat-form-field\r\n appearance=\"outline\"\r\n class=\"w-full\"\r\n [attr.style]=\"\r\n field.background\r\n ? '--dynamic-background-color: ' + field.background\r\n : 'transparent'\r\n \"\r\n >\r\n <mat-label style=\"font-size: 12px\">{{ field.label }}</mat-label>\r\n <input\r\n [matDatepicker]=\"datepickerRef\"\r\n [formControlName]=\"field.name!\"\r\n matInput\r\n (dateChange)=\"emitEvent(field, $event)\"\r\n [min]=\"field.minDate || null\"\r\n [max]=\"field.maxDate || null\"\r\n [matDatepickerFilter]=\"field.dateFilterFn!\"\r\n />\r\n <mat-datepicker-toggle\r\n [for]=\"datepickerRef\"\r\n class=\"block\"\r\n matSuffix\r\n ></mat-datepicker-toggle>\r\n <mat-datepicker\r\n #datepickerRef\r\n class=\"end-position\"\r\n ></mat-datepicker>\r\n\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </mat-form-field>\r\n </ng-template>\r\n\r\n <!-- DATE-RANGE -->\r\n <ng-template #rangeDate>\r\n <mat-form-field\r\n appearance=\"outline\"\r\n class=\"w-full\"\r\n [attr.style]=\"\r\n field.background\r\n ? '--dynamic-background-color: ' + field.background\r\n : 'transparent'\r\n \"\r\n >\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-date-range-input [rangePicker]=\"picker\">\r\n <input\r\n matStartDate\r\n placeholder=\"Fecha Inicial\"\r\n [formControlName]=\"field.name + 'start'\"\r\n (dateChange)=\"emitEvent(field, $event)\"\r\n />\r\n <input\r\n matEndDate\r\n placeholder=\"Fecha Final\"\r\n [formControlName]=\"field.name + 'end'\"\r\n />\r\n </mat-date-range-input>\r\n\r\n <mat-datepicker-toggle\r\n matIconSuffix\r\n [for]=\"picker\"\r\n ></mat-datepicker-toggle>\r\n <mat-date-range-picker #picker></mat-date-range-picker>\r\n\r\n <!-- MENSAJES DE ERROR -->\r\n <mat-error\r\n *ngIf=\"\r\n form.controls[field.name]?.invalid &&\r\n (form.controls[field.name]?.touched ||\r\n form.controls[field.name]?.dirty)\r\n \"\r\n >\r\n {{ getErrorMessage(field.name) }}\r\n </mat-error>\r\n </mat-form-field>\r\n </ng-template>\r\n\r\n <!-- TEXT-AREA -->\r\n <mat-form-field\r\n [attr.style]=\"\r\n field.background\r\n ? '--dynamic-background-color: ' + field.background\r\n : 'transparent'\r\n \"\r\n *ngSwitchCase=\"eDataType.tex_area\"\r\n appearance=\"outline\"\r\n class=\"w-full h-full\"\r\n >\r\n <mat-label>{{ field.label }}</mat-label>\r\n <textarea\r\n matInput\r\n #observaciones\r\n [name]=\"field.name!\"\r\n [placeholder]=\"field.plac