ui-core-abv 0.10.4 → 0.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/ui-core-abv.mjs +162 -23
- package/fesm2022/ui-core-abv.mjs.map +1 -1
- package/lib/components/dynamic-form/form-state.service.d.ts +14 -0
- package/lib/components/dynamic-form/form-wrapper/form-wrapper.component.d.ts +13 -0
- package/lib/components/dynamic-form/repeater/repeater.component.d.ts +8 -1
- package/package.json +1 -1
package/fesm2022/ui-core-abv.mjs
CHANGED
|
@@ -4175,6 +4175,24 @@ class UicFormStateService {
|
|
|
4175
4175
|
this.formChangesSub = null;
|
|
4176
4176
|
});
|
|
4177
4177
|
}
|
|
4178
|
+
/**
|
|
4179
|
+
* Opciones de subcampos de repeater resueltas por ítem (optionsSource con dependsOn).
|
|
4180
|
+
* Cada FormGroup de ítem guarda las suyas; el registro se reemplaza (inmutable) en cada cambio
|
|
4181
|
+
* para que los consumidores puedan memoizar por referencia.
|
|
4182
|
+
*/
|
|
4183
|
+
rowFieldOptions = new WeakMap();
|
|
4184
|
+
rowFieldOptionsVersion = signal(0);
|
|
4185
|
+
setRowFieldOptions(row, fieldName, state) {
|
|
4186
|
+
const current = this.rowFieldOptions.get(row) ?? {};
|
|
4187
|
+
this.rowFieldOptions.set(row, {
|
|
4188
|
+
...current,
|
|
4189
|
+
[fieldName]: { ...current[fieldName], ...state }
|
|
4190
|
+
});
|
|
4191
|
+
this.rowFieldOptionsVersion.update(version => version + 1);
|
|
4192
|
+
}
|
|
4193
|
+
getRowFieldOptions(row) {
|
|
4194
|
+
return this.rowFieldOptions.get(row);
|
|
4195
|
+
}
|
|
4178
4196
|
getFieldValue(fieldName) {
|
|
4179
4197
|
if (!this.currentForm)
|
|
4180
4198
|
return undefined;
|
|
@@ -4189,6 +4207,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
4189
4207
|
|
|
4190
4208
|
class UicRepeaterComponent {
|
|
4191
4209
|
fb = inject(FormBuilder);
|
|
4210
|
+
formState = inject(UicFormStateService, { optional: true });
|
|
4211
|
+
itemFieldsCache = new WeakMap();
|
|
4192
4212
|
formArray;
|
|
4193
4213
|
field;
|
|
4194
4214
|
cols = 2;
|
|
@@ -4210,6 +4230,31 @@ class UicRepeaterComponent {
|
|
|
4210
4230
|
}
|
|
4211
4231
|
}
|
|
4212
4232
|
}
|
|
4233
|
+
/**
|
|
4234
|
+
* Subcampos del ítem con las opciones resueltas para ese ítem (optionsSource con dependsOn).
|
|
4235
|
+
* Se memoiza por referencia para no recrear los FormField en cada ciclo de detección.
|
|
4236
|
+
*/
|
|
4237
|
+
getItemFields(control) {
|
|
4238
|
+
const base = this.field.repeaterFields ?? [];
|
|
4239
|
+
this.formState?.rowFieldOptionsVersion();
|
|
4240
|
+
const row = control;
|
|
4241
|
+
const state = this.formState?.getRowFieldOptions(row);
|
|
4242
|
+
const cached = this.itemFieldsCache.get(row);
|
|
4243
|
+
if (cached && cached.base === base && cached.state === state)
|
|
4244
|
+
return cached.fields;
|
|
4245
|
+
const fields = !state ? base : base.map(subField => {
|
|
4246
|
+
const rowState = state[subField.name];
|
|
4247
|
+
if (!rowState)
|
|
4248
|
+
return subField;
|
|
4249
|
+
return {
|
|
4250
|
+
...subField,
|
|
4251
|
+
...(rowState.options !== undefined ? { options: rowState.options } : {}),
|
|
4252
|
+
...(rowState.loading !== undefined ? { loading: rowState.loading } : {})
|
|
4253
|
+
};
|
|
4254
|
+
});
|
|
4255
|
+
this.itemFieldsCache.set(row, { base, state, fields });
|
|
4256
|
+
return fields;
|
|
4257
|
+
}
|
|
4213
4258
|
getGroup(index) {
|
|
4214
4259
|
return this.formArray.at(index);
|
|
4215
4260
|
}
|
|
@@ -4279,7 +4324,7 @@ class UicRepeaterComponent {
|
|
|
4279
4324
|
}
|
|
4280
4325
|
}
|
|
4281
4326
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicRepeaterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4282
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicRepeaterComponent, isStandalone: true, selector: "ui-repeater", inputs: { formArray: "formArray", field: "field", cols: "cols", error: "error", fileUidResolverFn: "fileUidResolverFn", selectOptionsResolver: "selectOptionsResolver", parentForm: "parentForm" }, ngImport: i0, template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <div class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </div>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"
|
|
4327
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicRepeaterComponent, isStandalone: true, selector: "ui-repeater", inputs: { formArray: "formArray", field: "field", cols: "cols", error: "error", fileUidResolverFn: "fileUidResolverFn", selectOptionsResolver: "selectOptionsResolver", parentForm: "parentForm" }, ngImport: i0, template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <div class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </div>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"getItemFields(group)\" \r\n [disabled]=\"formArray.disabled\"\r\n [cols]=\"cols\"\r\n [fileUidResolverFn]=\"fileUidResolverFn\"\r\n [selectOptionsResolver]=\"selectOptionsResolver\"\r\n [parentFormValues]=\"getParentFormValues()\"\r\n [isSubForm]=\"true\">\r\n </ui-dynamic-form>\r\n </div>\r\n </div>\r\n }\r\n \r\n @if (canAdd && !formArray.disabled) {\r\n <div class=\"uic-repeater-add-action\">\r\n <ui-button \r\n [icon]=\"'ri-add-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'primary'\" \r\n (click)=\"addGroup()\"\r\n [text]=\"field.repeaterAddLabel || 'A\u00F1adir'\">\r\n </ui-button>\r\n </div>\r\n }\r\n @if (error) {\r\n <p class=\"uic-repeater-error\">{{ error }}</p>\r\n }\r\n</div>\r\n", styles: [".uic-repeater{display:flex;flex-direction:column;gap:16px;width:100%}.uic-repeater.disabled{opacity:.7}.uic-repeater .uic-repeater-item{border:1px solid var(--primary-400, #e0e0e0);border-radius:8px;background-color:var(--uic-bg-color, #fff)}.uic-repeater .uic-repeater-item .uic-repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;border-bottom:1px solid var(--primary-400);padding:5px 10px;min-height:34px}.uic-repeater .uic-repeater-item .uic-repeater-item-header .uic-repeater-item-title{margin:0;font-size:14px;font-weight:600;color:var(--primary-700)}.uic-repeater .uic-repeater-item .uic-repeater-item-body{width:100%;padding:0 12px}.uic-repeater .uic-repeater-add-action{margin-top:1px}.uic-repeater .uic-repeater-error{margin:4px 0 0;font-size:12px;color:var(--uic-red, #e53935)}.repeater-label{font-size:max(var(--form-ref) + 4px,14px);line-height:1.125rem;min-height:1.125rem;font-weight:400;color:var(--grey-950);margin-bottom:var(--input-label-space)}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "component", type: i0.forwardRef(() => UicButtonComponent), selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: i0.forwardRef(() => UicDynamicFormComponent), selector: "ui-dynamic-form", inputs: ["fields", "isSubForm", "form", "disabled", "externalValidationStates", "voiceToTextSilenceMs", "cols", "iconMode", "fileUidResolverFn", "selectOptionsResolver", "parentFormValues"], outputs: ["fieldBlur"] }] });
|
|
4283
4328
|
}
|
|
4284
4329
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicRepeaterComponent, decorators: [{
|
|
4285
4330
|
type: Component,
|
|
@@ -4288,7 +4333,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
4288
4333
|
ReactiveFormsModule,
|
|
4289
4334
|
UicButtonComponent,
|
|
4290
4335
|
forwardRef(() => UicDynamicFormComponent)
|
|
4291
|
-
], template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <div class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </div>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"
|
|
4336
|
+
], template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <div class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </div>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"getItemFields(group)\" \r\n [disabled]=\"formArray.disabled\"\r\n [cols]=\"cols\"\r\n [fileUidResolverFn]=\"fileUidResolverFn\"\r\n [selectOptionsResolver]=\"selectOptionsResolver\"\r\n [parentFormValues]=\"getParentFormValues()\"\r\n [isSubForm]=\"true\">\r\n </ui-dynamic-form>\r\n </div>\r\n </div>\r\n }\r\n \r\n @if (canAdd && !formArray.disabled) {\r\n <div class=\"uic-repeater-add-action\">\r\n <ui-button \r\n [icon]=\"'ri-add-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'primary'\" \r\n (click)=\"addGroup()\"\r\n [text]=\"field.repeaterAddLabel || 'A\u00F1adir'\">\r\n </ui-button>\r\n </div>\r\n }\r\n @if (error) {\r\n <p class=\"uic-repeater-error\">{{ error }}</p>\r\n }\r\n</div>\r\n", styles: [".uic-repeater{display:flex;flex-direction:column;gap:16px;width:100%}.uic-repeater.disabled{opacity:.7}.uic-repeater .uic-repeater-item{border:1px solid var(--primary-400, #e0e0e0);border-radius:8px;background-color:var(--uic-bg-color, #fff)}.uic-repeater .uic-repeater-item .uic-repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;border-bottom:1px solid var(--primary-400);padding:5px 10px;min-height:34px}.uic-repeater .uic-repeater-item .uic-repeater-item-header .uic-repeater-item-title{margin:0;font-size:14px;font-weight:600;color:var(--primary-700)}.uic-repeater .uic-repeater-item .uic-repeater-item-body{width:100%;padding:0 12px}.uic-repeater .uic-repeater-add-action{margin-top:1px}.uic-repeater .uic-repeater-error{margin:4px 0 0;font-size:12px;color:var(--uic-red, #e53935)}.repeater-label{font-size:max(var(--form-ref) + 4px,14px);line-height:1.125rem;min-height:1.125rem;font-weight:400;color:var(--grey-950);margin-bottom:var(--input-label-space)}\n"] }]
|
|
4292
4337
|
}], propDecorators: { formArray: [{
|
|
4293
4338
|
type: Input,
|
|
4294
4339
|
args: [{ required: true }]
|
|
@@ -5055,6 +5100,9 @@ class UicFormWrapperComponent {
|
|
|
5055
5100
|
formValueSub = null;
|
|
5056
5101
|
optionSourceSubs = new Map();
|
|
5057
5102
|
optionSourceDependencyValueByField = new Map();
|
|
5103
|
+
rowOptionSourceSubs = new Map();
|
|
5104
|
+
rowOptionSourceDependencyKeys = new WeakMap();
|
|
5105
|
+
formState = inject(UicFormStateService);
|
|
5058
5106
|
computedFieldSubs = new Map();
|
|
5059
5107
|
computedFieldDependencySignatureByField = new Map();
|
|
5060
5108
|
computedExternalValidationLastValue = new Map();
|
|
@@ -5366,12 +5414,106 @@ class UicFormWrapperComponent {
|
|
|
5366
5414
|
if (field.type !== 'repeater' || !field.repeaterFields?.length)
|
|
5367
5415
|
continue;
|
|
5368
5416
|
field.repeaterFields.forEach(subField => {
|
|
5369
|
-
|
|
5417
|
+
// Con dependsOn se resuelve por ítem (ver updateRowScopedSubFieldOptionsSources).
|
|
5418
|
+
if (this.hasUsableOptionsSource(subField) && !subField.optionsSource?.dependsOn) {
|
|
5370
5419
|
this.resolveRepeaterSubFieldOptionsSource(field, subField, force);
|
|
5371
5420
|
}
|
|
5372
5421
|
});
|
|
5373
5422
|
}
|
|
5374
5423
|
}
|
|
5424
|
+
this.updateRowScopedSubFieldOptionsSources(force);
|
|
5425
|
+
}
|
|
5426
|
+
/**
|
|
5427
|
+
* Subcampos con optionsSource.dependsOn: cada ítem resuelve sus propias opciones.
|
|
5428
|
+
* La dependencia sigue la regla de ámbito "gana el hermano": primero el ítem, luego el raíz
|
|
5429
|
+
* (las relaciones van de menor a mayor o igual ámbito, nunca raíz -> ítem).
|
|
5430
|
+
*/
|
|
5431
|
+
updateRowScopedSubFieldOptionsSources(force = false) {
|
|
5432
|
+
const activeRows = new Set();
|
|
5433
|
+
for (const block of this.effectiveSchema?.blocks ?? []) {
|
|
5434
|
+
for (const field of block.fields) {
|
|
5435
|
+
if (field.type !== 'repeater' || !field.repeaterFields?.length)
|
|
5436
|
+
continue;
|
|
5437
|
+
const dependentSubFields = field.repeaterFields.filter(subField => this.hasUsableOptionsSource(subField) && !!subField.optionsSource?.dependsOn);
|
|
5438
|
+
if (!dependentSubFields.length)
|
|
5439
|
+
continue;
|
|
5440
|
+
const formArray = this.form?.get(field.name);
|
|
5441
|
+
if (!formArray)
|
|
5442
|
+
continue;
|
|
5443
|
+
const rootValues = this.toNestedValues(this.form.getRawValue());
|
|
5444
|
+
formArray.controls.forEach(control => {
|
|
5445
|
+
const row = control;
|
|
5446
|
+
activeRows.add(row);
|
|
5447
|
+
dependentSubFields.forEach(subField => this.resolveRowSubFieldOptionsSource(row, subField, rootValues, force));
|
|
5448
|
+
});
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
for (const [row, subs] of this.rowOptionSourceSubs) {
|
|
5452
|
+
if (activeRows.has(row))
|
|
5453
|
+
continue;
|
|
5454
|
+
subs.forEach(sub => sub.unsubscribe());
|
|
5455
|
+
this.rowOptionSourceSubs.delete(row);
|
|
5456
|
+
}
|
|
5457
|
+
}
|
|
5458
|
+
resolveRowSubFieldOptionsSource(row, subField, rootValues, force) {
|
|
5459
|
+
const source = subField.optionsSource;
|
|
5460
|
+
const dependsOn = source.dependsOn;
|
|
5461
|
+
if (!this.selectOptionsResolver) {
|
|
5462
|
+
this.formState.setRowFieldOptions(row, subField.name, { options: subField.options ?? [], loading: false });
|
|
5463
|
+
return;
|
|
5464
|
+
}
|
|
5465
|
+
const rowValues = this.toNestedValues(row.getRawValue());
|
|
5466
|
+
const dependencyValue = row.contains(dependsOn)
|
|
5467
|
+
? row.get([dependsOn])?.value
|
|
5468
|
+
: this.getValueByPath(rootValues, dependsOn);
|
|
5469
|
+
const dependencyKey = `${dependsOn}:${this.safeStringify(dependencyValue ?? null)}`;
|
|
5470
|
+
let dependencyKeys = this.rowOptionSourceDependencyKeys.get(row);
|
|
5471
|
+
if (!dependencyKeys) {
|
|
5472
|
+
dependencyKeys = new Map();
|
|
5473
|
+
this.rowOptionSourceDependencyKeys.set(row, dependencyKeys);
|
|
5474
|
+
}
|
|
5475
|
+
if (!force && dependencyKeys.get(subField.name) === dependencyKey)
|
|
5476
|
+
return;
|
|
5477
|
+
dependencyKeys.set(subField.name, dependencyKey);
|
|
5478
|
+
let subs = this.rowOptionSourceSubs.get(row);
|
|
5479
|
+
if (!subs) {
|
|
5480
|
+
subs = new Map();
|
|
5481
|
+
this.rowOptionSourceSubs.set(row, subs);
|
|
5482
|
+
}
|
|
5483
|
+
subs.get(subField.name)?.unsubscribe();
|
|
5484
|
+
subs.delete(subField.name);
|
|
5485
|
+
const control = row.get([subField.name]);
|
|
5486
|
+
if (dependencyValue === null || dependencyValue === undefined || dependencyValue === '') {
|
|
5487
|
+
if (control)
|
|
5488
|
+
this.clearControlValue(control, subField);
|
|
5489
|
+
this.formState.setRowFieldOptions(row, subField.name, { options: [], loading: false });
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
this.formState.setRowFieldOptions(row, subField.name, { loading: true });
|
|
5493
|
+
const values = { ...rootValues, ...rowValues };
|
|
5494
|
+
let optionsResult;
|
|
5495
|
+
try {
|
|
5496
|
+
optionsResult = this.selectOptionsResolver(source, { field: subField, values, dependencyValue });
|
|
5497
|
+
}
|
|
5498
|
+
catch (error) {
|
|
5499
|
+
this.handleRowOptionsSourceError(row, subField, source, error);
|
|
5500
|
+
return;
|
|
5501
|
+
}
|
|
5502
|
+
const sub = this.toObservable(optionsResult).subscribe({
|
|
5503
|
+
next: items => {
|
|
5504
|
+
const options = this.mapOptionsSourceItems(items, source);
|
|
5505
|
+
this.formState.setRowFieldOptions(row, subField.name, { options, loading: false });
|
|
5506
|
+
if (control)
|
|
5507
|
+
this.clearInvalidControlValue(control, options);
|
|
5508
|
+
},
|
|
5509
|
+
error: (error) => this.handleRowOptionsSourceError(row, subField, source, error),
|
|
5510
|
+
complete: () => this.formState.setRowFieldOptions(row, subField.name, { loading: false })
|
|
5511
|
+
});
|
|
5512
|
+
subs.set(subField.name, sub);
|
|
5513
|
+
}
|
|
5514
|
+
handleRowOptionsSourceError(row, subField, source, error) {
|
|
5515
|
+
this.formState.setRowFieldOptions(row, subField.name, { options: [], loading: false });
|
|
5516
|
+
this.optionsSourceError.emit({ field: subField, source, error });
|
|
5375
5517
|
}
|
|
5376
5518
|
resolveRepeaterSubFieldOptionsSource(parentField, subField, force = false) {
|
|
5377
5519
|
const source = subField.optionsSource;
|
|
@@ -5443,17 +5585,8 @@ class UicFormWrapperComponent {
|
|
|
5443
5585
|
this.updateDependentRepeaterSubFieldOptionsSources();
|
|
5444
5586
|
}
|
|
5445
5587
|
updateDependentRepeaterSubFieldOptionsSources() {
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
if (field.type !== 'repeater' || !field.repeaterFields?.length)
|
|
5449
|
-
continue;
|
|
5450
|
-
field.repeaterFields.forEach(subField => {
|
|
5451
|
-
if (this.hasUsableOptionsSource(subField) && subField.optionsSource?.dependsOn) {
|
|
5452
|
-
this.resolveRepeaterSubFieldOptionsSource(field, subField, true);
|
|
5453
|
-
}
|
|
5454
|
-
});
|
|
5455
|
-
}
|
|
5456
|
-
}
|
|
5588
|
+
// Sin force: cada ítem solo vuelve a resolver si cambió el valor de su dependencia.
|
|
5589
|
+
this.updateRowScopedSubFieldOptionsSources();
|
|
5457
5590
|
}
|
|
5458
5591
|
resolveOptionsSourceField(field, force = false) {
|
|
5459
5592
|
const source = field.optionsSource;
|
|
@@ -5578,6 +5711,9 @@ class UicFormWrapperComponent {
|
|
|
5578
5711
|
const control = this.getFieldControl(fieldName);
|
|
5579
5712
|
if (!control)
|
|
5580
5713
|
return;
|
|
5714
|
+
this.clearInvalidControlValue(control, options);
|
|
5715
|
+
}
|
|
5716
|
+
clearInvalidControlValue(control, options) {
|
|
5581
5717
|
const currentValue = control.value;
|
|
5582
5718
|
if (currentValue === null || currentValue === undefined || currentValue === '')
|
|
5583
5719
|
return;
|
|
@@ -5599,6 +5735,9 @@ class UicFormWrapperComponent {
|
|
|
5599
5735
|
const control = this.getFieldControl(field.name);
|
|
5600
5736
|
if (!control)
|
|
5601
5737
|
return;
|
|
5738
|
+
this.clearControlValue(control, field);
|
|
5739
|
+
}
|
|
5740
|
+
clearControlValue(control, field) {
|
|
5602
5741
|
const blankValue = this.blankValue(field);
|
|
5603
5742
|
if (control.value !== blankValue) {
|
|
5604
5743
|
control.setValue(blankValue, { emitEvent: false });
|
|
@@ -5631,6 +5770,8 @@ class UicFormWrapperComponent {
|
|
|
5631
5770
|
this.optionSourceSubs.forEach(sub => sub.unsubscribe());
|
|
5632
5771
|
this.optionSourceSubs.clear();
|
|
5633
5772
|
this.optionSourceDependencyValueByField.clear();
|
|
5773
|
+
this.rowOptionSourceSubs.forEach(subs => subs.forEach(sub => sub.unsubscribe()));
|
|
5774
|
+
this.rowOptionSourceSubs.clear();
|
|
5634
5775
|
}
|
|
5635
5776
|
clearComputedFieldSubs() {
|
|
5636
5777
|
this.computedFieldSubs.forEach(sub => sub.unsubscribe());
|
|
@@ -7544,13 +7685,12 @@ function helperShowFormFromBuilder(blocks, cols, editable = false, schemaOptions
|
|
|
7544
7685
|
};
|
|
7545
7686
|
}
|
|
7546
7687
|
function mapEditableBlocksToBlocks(blocks, editable) {
|
|
7688
|
+
// Solo campos raíz: un campo raíz nunca depende de un subcampo (raíz -> ítem no existe),
|
|
7689
|
+
// y los códigos sf_N se repiten entre repeaters, así que no pueden ir en un mapa global.
|
|
7547
7690
|
const fieldCodeToName = new Map();
|
|
7548
7691
|
blocks.forEach(block => {
|
|
7549
7692
|
block.fields.forEach(field => {
|
|
7550
7693
|
fieldCodeToName.set(field.code, field.fieldData.name);
|
|
7551
|
-
field.repeaterSubFields?.forEach(sf => {
|
|
7552
|
-
fieldCodeToName.set(sf.code, sf.fieldData.name);
|
|
7553
|
-
});
|
|
7554
7694
|
});
|
|
7555
7695
|
});
|
|
7556
7696
|
return blocks.map(block => ({
|
|
@@ -7569,9 +7709,9 @@ function mapEditableBlocksToBlocks(blocks, editable) {
|
|
|
7569
7709
|
}
|
|
7570
7710
|
: field.fieldData.optionsSource;
|
|
7571
7711
|
const isComputedField = field.fieldData.type === 'computed';
|
|
7572
|
-
//
|
|
7573
|
-
// se resuelven primero contra los hermanos de su propio repeater.
|
|
7712
|
+
// Referencias de un subcampo: gana el hermano del mismo repeater, luego el raíz.
|
|
7574
7713
|
const siblingCodeToName = new Map((field.repeaterSubFields ?? []).map(sf => [sf.code, sf.fieldData.name]));
|
|
7714
|
+
const resolveSubFieldRef = (code) => siblingCodeToName.get(code) ?? fieldCodeToName.get(code) ?? code;
|
|
7575
7715
|
return {
|
|
7576
7716
|
...field.fieldData,
|
|
7577
7717
|
type: isComputedField ? 'text' : field.fieldData.type,
|
|
@@ -7590,13 +7730,12 @@ function mapEditableBlocksToBlocks(blocks, editable) {
|
|
|
7590
7730
|
const sfIsComputed = sf.fieldData.type === 'computed';
|
|
7591
7731
|
const sfVisibilityRules = sf.fieldData.visibilityRules?.map(rule => ({
|
|
7592
7732
|
...rule,
|
|
7593
|
-
fieldName:
|
|
7733
|
+
fieldName: resolveSubFieldRef(rule.fieldName)
|
|
7594
7734
|
}));
|
|
7595
7735
|
const sfOptionsSource = sf.fieldData.optionsSource?.dependsOn
|
|
7596
7736
|
? {
|
|
7597
7737
|
...sf.fieldData.optionsSource,
|
|
7598
|
-
dependsOn:
|
|
7599
|
-
?? sf.fieldData.optionsSource.dependsOn
|
|
7738
|
+
dependsOn: resolveSubFieldRef(sf.fieldData.optionsSource.dependsOn)
|
|
7600
7739
|
}
|
|
7601
7740
|
: sf.fieldData.optionsSource;
|
|
7602
7741
|
return {
|
|
@@ -7609,7 +7748,7 @@ function mapEditableBlocksToBlocks(blocks, editable) {
|
|
|
7609
7748
|
? {
|
|
7610
7749
|
computedSource: {
|
|
7611
7750
|
key: sf.fieldData.computedSource.key,
|
|
7612
|
-
dependsOn: (sf.fieldData.computedSource.dependsOn ?? []).map(
|
|
7751
|
+
dependsOn: (sf.fieldData.computedSource.dependsOn ?? []).map(resolveSubFieldRef)
|
|
7613
7752
|
}
|
|
7614
7753
|
}
|
|
7615
7754
|
: {})
|