@stemy/ngx-dynamic-form 13.3.12 → 19.0.1

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.
Files changed (27) hide show
  1. package/fesm2022/stemy-ngx-dynamic-form.mjs +1993 -0
  2. package/fesm2022/stemy-ngx-dynamic-form.mjs.map +1 -0
  3. package/index.d.ts +5 -0
  4. package/ngx-dynamic-form/common-types.d.ts +38 -38
  5. package/ngx-dynamic-form/components/base/dynamic-base-form-array.component.d.ts +38 -38
  6. package/ngx-dynamic-form/components/base/dynamic-base-form-control-container.component.d.ts +39 -39
  7. package/ngx-dynamic-form/components/base/dynamic-base-form-control.component.d.ts +26 -26
  8. package/ngx-dynamic-form/components/base/dynamic-base-form-group.component.d.ts +25 -25
  9. package/ngx-dynamic-form/components/base/dynamic-base-form.component.d.ts +48 -48
  10. package/ngx-dynamic-form/components/base/dynamic-base-select.component.d.ts +15 -16
  11. package/ngx-dynamic-form/directives/async-submit.directive.d.ts +30 -30
  12. package/ngx-dynamic-form/ngx-dynamic-form.imports.d.ts +12 -12
  13. package/ngx-dynamic-form/ngx-dynamic-form.module.d.ts +19 -19
  14. package/ngx-dynamic-form/services/dynamic-form.service.d.ts +58 -58
  15. package/ngx-dynamic-form/utils/creators.d.ts +17 -17
  16. package/ngx-dynamic-form/utils/customizer.d.ts +14 -14
  17. package/ngx-dynamic-form/utils/dynamic-editor.model.d.ts +11 -11
  18. package/ngx-dynamic-form/utils/dynamic-form-array.model.d.ts +69 -69
  19. package/ngx-dynamic-form/utils/dynamic-form-group.model.d.ts +14 -14
  20. package/ngx-dynamic-form/utils/dynamic-select.model.d.ts +39 -39
  21. package/ngx-dynamic-form/utils/form-select-subject.d.ts +6 -6
  22. package/ngx-dynamic-form/utils/form-subject.d.ts +10 -10
  23. package/ngx-dynamic-form/utils/misc.d.ts +11 -11
  24. package/ngx-dynamic-form/utils/validation-errors.d.ts +11 -11
  25. package/ngx-dynamic-form/utils/validators.d.ts +8 -8
  26. package/package.json +23 -32
  27. package/public_api.d.ts +21 -21
@@ -0,0 +1,1993 @@
1
+ import * as i1 from '@ng-dynamic-forms/core';
2
+ import { DynamicInputControlModel, DYNAMIC_FORM_CONTROL_TYPE_EDITOR, serializable, DynamicFormArrayGroupModel as DynamicFormArrayGroupModel$1, DynamicFormArrayModel as DynamicFormArrayModel$1, DynamicFormGroupModel as DynamicFormGroupModel$1, DynamicFormOption as DynamicFormOption$1, DynamicSelectModel as DynamicSelectModel$1, DynamicCheckboxModel, DynamicDatePickerModel, DynamicInputModel, DynamicTextAreaModel, DynamicFileUploadModel, DynamicFormService as DynamicFormService$1, DynamicFormValueControlModel, DynamicFormComponent, DynamicTemplateDirective, DynamicFormControlContainerComponent, DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DynamicFormArrayComponent, DynamicFormControlComponent, DynamicFormGroupComponent, DYNAMIC_FORM_CONTROL_MAP_FN, DYNAMIC_VALIDATORS } from '@ng-dynamic-forms/core';
3
+ export { DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, DYNAMIC_FORM_CONTROL_TYPE_EDITOR, DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD, DYNAMIC_FORM_CONTROL_TYPE_GROUP, DYNAMIC_FORM_CONTROL_TYPE_INPUT, DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, DYNAMIC_FORM_CONTROL_TYPE_SELECT, DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, DynamicCheckboxGroupModel, DynamicCheckboxModel, DynamicDatePickerModel, DynamicFileUploadModel, DynamicFormControlComponent, DynamicFormControlModel, DynamicFormsCoreModule, DynamicInputModel, DynamicListDirective, DynamicRadioGroupModel, DynamicTemplateDirective, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
4
+ import * as i2 from '@stemy/ngx-utils';
5
+ import { ObjectUtils, TimerUtils, cachedFactory, StringUtils, OpenApiService, TOASTER_SERVICE, ObservableUtils, EventsService, NgxUtilsModule } from '@stemy/ngx-utils';
6
+ import { __decorate } from 'tslib';
7
+ import { BehaviorSubject, of, isObservable, Subject, firstValueFrom, Subscription } from 'rxjs';
8
+ import { map, debounceTime, groupBy, mergeMap, first } from 'rxjs/operators';
9
+ import * as i0 from '@angular/core';
10
+ import { Injector, Injectable, Inject, EventEmitter, Directive, Input, Output, HostBinding, HostListener, Component, ChangeDetectionStrategy, ContentChildren, ViewChildren, ViewContainerRef, ViewChild, forwardRef, NgModule } from '@angular/core';
11
+ import { FormGroup, FormArray, FormsModule, ReactiveFormsModule, NG_VALIDATORS } from '@angular/forms';
12
+ import { CommonModule } from '@angular/common';
13
+
14
+ class DynamicEditorModel extends DynamicInputControlModel {
15
+ type = DYNAMIC_FORM_CONTROL_TYPE_EDITOR;
16
+ inputType;
17
+ convertObject;
18
+ constructor(config, layout) {
19
+ super(config, layout);
20
+ this.inputType = config.inputType || "javascript";
21
+ this.convertObject = config.convertObject !== false;
22
+ }
23
+ }
24
+ __decorate([
25
+ serializable()
26
+ ], DynamicEditorModel.prototype, "type", void 0);
27
+
28
+ class DynamicFormArrayGroupModel extends DynamicFormArrayGroupModel$1 {
29
+ context;
30
+ get hidden() {
31
+ return this.isHidden;
32
+ }
33
+ set hidden(value) {
34
+ if (this.isHidden == value)
35
+ return;
36
+ this.isHidden = value || false;
37
+ this.context?.filterGroups();
38
+ }
39
+ isHidden;
40
+ constructor(context, group, index = -1) {
41
+ super(context, group, index);
42
+ this.context = context;
43
+ this.isHidden = false;
44
+ }
45
+ }
46
+ class DynamicFormArrayModel extends DynamicFormArrayModel$1 {
47
+ config;
48
+ filteredGroups;
49
+ sortable;
50
+ useTabs;
51
+ saveTab;
52
+ restoreTab;
53
+ getTabLabel;
54
+ additional;
55
+ groups = [];
56
+ tabIndex = 0;
57
+ _sortBy;
58
+ _sortDescending;
59
+ _formArray;
60
+ _filteredGroups;
61
+ _filterTimer;
62
+ get addItem() {
63
+ return this.config.addItem !== false;
64
+ }
65
+ get insertItem() {
66
+ return !this._sortBy && this.config.insertItem !== false;
67
+ }
68
+ get cloneItem() {
69
+ return this.config.cloneItem !== false;
70
+ }
71
+ get moveItem() {
72
+ return !this._sortBy && this.config.moveItem !== false;
73
+ }
74
+ get removeItem() {
75
+ return this.config.removeItem !== false;
76
+ }
77
+ get clearItems() {
78
+ return this.config.clearItems !== false;
79
+ }
80
+ get sortBy() {
81
+ return this._sortBy;
82
+ }
83
+ set sortBy(value) {
84
+ if (!this.sortable)
85
+ return;
86
+ value = value || null;
87
+ if (this._sortBy !== value) {
88
+ this._sortBy = value;
89
+ this._sortDescending = false;
90
+ }
91
+ else if (this._sortDescending) {
92
+ this._sortBy = null;
93
+ this._sortDescending = false;
94
+ }
95
+ else {
96
+ this._sortDescending = true;
97
+ }
98
+ this.filterGroups();
99
+ }
100
+ get sortDescending() {
101
+ return this._sortDescending;
102
+ }
103
+ get sortOrder() {
104
+ return this.sortDescending ? "desc" : "asc";
105
+ }
106
+ constructor(config, layout) {
107
+ super(config, layout);
108
+ this.config = config;
109
+ this.filteredGroups = new BehaviorSubject([]);
110
+ this.sortable = config.sortable || false;
111
+ this.useTabs = config.useTabs || false;
112
+ this.saveTab = ObjectUtils.isFunction(config.saveTab) ? config.saveTab : ((index, model, arrayModel) => {
113
+ arrayModel.tabIndex = index;
114
+ });
115
+ this.restoreTab = ObjectUtils.isFunction(config.restoreTab) ? config.restoreTab : ((model) => {
116
+ return model.tabIndex;
117
+ });
118
+ this.getTabLabel = ObjectUtils.isFunction(config.getTabLabel) ? config.getTabLabel : ((index) => {
119
+ return `${index + 1}`;
120
+ });
121
+ this.additional = config.additional || {};
122
+ this.tabIndex = 0;
123
+ this._sortBy = null;
124
+ this._sortDescending = false;
125
+ this._formArray = null;
126
+ }
127
+ initialize(array) {
128
+ this._formArray = array || this._formArray;
129
+ this.filterGroups();
130
+ }
131
+ filterGroups() {
132
+ this._filterTimer = this._filterTimer || TimerUtils.createTimeout();
133
+ this._filterTimer.set(() => {
134
+ const filtered = this.groups.filter(g => !g.hidden);
135
+ if (this._sortBy && this._formArray) {
136
+ const compare = this._sortDescending
137
+ ? (a, b) => this.compareModels(b, a)
138
+ : (a, b) => this.compareModels(a, b);
139
+ filtered.sort(compare);
140
+ }
141
+ this._filteredGroups = filtered;
142
+ this.filteredGroups.next(filtered);
143
+ }, 100);
144
+ }
145
+ getFiltered(index) {
146
+ return !this._filteredGroups ? null : this._filteredGroups[index];
147
+ }
148
+ insertGroup(index) {
149
+ const group = new DynamicFormArrayGroupModel(this, this.groupFactory());
150
+ this.groups.splice(index, 0, group);
151
+ this.groups.forEach((g, index) => g.index = index);
152
+ this.filterGroups();
153
+ return group;
154
+ }
155
+ moveGroup(index, step) {
156
+ super.moveGroup(index, step);
157
+ this.filterGroups();
158
+ }
159
+ removeGroup(index) {
160
+ super.removeGroup(index);
161
+ this.filterGroups();
162
+ }
163
+ compareModels(a, b) {
164
+ const aGroup = this._formArray.at(a.index).get(this._sortBy)?.value || null;
165
+ const bGroup = this._formArray.at(b.index).get(this._sortBy)?.value || null;
166
+ return ObjectUtils.compare(aGroup, bGroup);
167
+ }
168
+ }
169
+
170
+ class DynamicFormGroupModel extends DynamicFormGroupModel$1 {
171
+ groups;
172
+ constructor(config, layout) {
173
+ super(config, layout);
174
+ const controls = [...config.group];
175
+ const groups = [];
176
+ const sets = config.fieldSets || [];
177
+ for (const fs of sets) {
178
+ const fields = [];
179
+ for (const f of fs.fields) {
180
+ const ix = controls.findIndex(c => c.id === f);
181
+ if (ix < 0)
182
+ continue;
183
+ fields.push(controls.splice(ix, 1)[0]);
184
+ }
185
+ if (fields.length === 0)
186
+ continue;
187
+ groups.push({ id: fs.id, legend: fs.legend, fields });
188
+ }
189
+ if (controls.length > 0) {
190
+ groups.unshift({ id: "root-controls", legend: config.legend, fields: controls });
191
+ }
192
+ this.groups = groups;
193
+ }
194
+ }
195
+
196
+ const ignoredKeys = ["disabled", "label", "value", "classes"];
197
+ class DynamicFormOption extends DynamicFormOption$1 {
198
+ classes;
199
+ props;
200
+ constructor(config) {
201
+ super(config);
202
+ this.classes = config.classes || "";
203
+ this.props = Object.keys(config).reduce((res, k) => {
204
+ if (ignoredKeys.indexOf(k) >= 0)
205
+ return res;
206
+ res[k] = config[k];
207
+ return res;
208
+ }, {});
209
+ }
210
+ }
211
+ class DynamicSelectModel extends DynamicSelectModel$1 {
212
+ groupBy;
213
+ inline;
214
+ getClasses;
215
+ allowEmpty;
216
+ options$ = null;
217
+ mOptions;
218
+ constructor(config, layout) {
219
+ super(config, layout);
220
+ this.groupBy = config.groupBy || null;
221
+ this.inline = config.inline || false;
222
+ this.getClasses = ObjectUtils.isFunction(config.getClasses) ? config.getClasses : (() => "");
223
+ this.allowEmpty = config.allowEmpty || false;
224
+ this.mOptions = this.mOptions || [];
225
+ }
226
+ updateOptions() {
227
+ this.options$ = of(this.mOptions);
228
+ }
229
+ set options(options) {
230
+ if (Array.isArray(options)) {
231
+ this.mOptions = options.map(optionConfig => new DynamicFormOption(optionConfig));
232
+ this.updateOptions();
233
+ }
234
+ else if (isObservable(options)) {
235
+ this.options$ = options.pipe(map(optionsConfig => {
236
+ this.mOptions = optionsConfig.map(optionConfig => new DynamicFormOption(optionConfig));
237
+ return this.mOptions;
238
+ }));
239
+ }
240
+ else {
241
+ this.updateOptions();
242
+ }
243
+ }
244
+ get options() {
245
+ return this.mOptions;
246
+ }
247
+ insert(index, optionConfig) {
248
+ const option = new DynamicFormOption(optionConfig);
249
+ this.mOptions.splice(index, 0, option);
250
+ this.updateOptions();
251
+ return option;
252
+ }
253
+ remove(...indices) {
254
+ indices.forEach(index => this.mOptions.splice(index, 1));
255
+ this.updateOptions();
256
+ }
257
+ }
258
+
259
+ function createFormConfig(id, config) {
260
+ const res = (config || { id });
261
+ res.id = id;
262
+ res.label = ObjectUtils.isNullOrUndefined(config.label) ? id : config.label;
263
+ res.disabled = config.disabled || false;
264
+ res.hidden = config.hidden || false;
265
+ return res;
266
+ }
267
+ function createFormCheckbox(id, config, layout) {
268
+ const res = createFormConfig(id, config);
269
+ res.indeterminate = config.indeterminate || false;
270
+ return new DynamicCheckboxModel(res, layout);
271
+ }
272
+ function createFormDate(id, config, layout) {
273
+ const res = createFormConfig(id, config);
274
+ res.autoFocus = config.autoFocus || false;
275
+ res.focusedDate = config.focusedDate || new Date();
276
+ res.inline = config.inline || false;
277
+ return new DynamicDatePickerModel(res, layout);
278
+ }
279
+ function createFormEditor(id, config, layout) {
280
+ const res = createFormConfig(id, config);
281
+ return new DynamicEditorModel(res, layout);
282
+ }
283
+ function createFormArray(id, config, layout) {
284
+ const res = createFormConfig(id, config);
285
+ return new DynamicFormArrayModel(res, layout);
286
+ }
287
+ function createFormGroup(id, config, layout) {
288
+ const res = createFormConfig(id, config);
289
+ res.name = config.name || "";
290
+ return new DynamicFormGroupModel(res, layout);
291
+ }
292
+ function createFormInput(id, config, type = "text", layout) {
293
+ const res = createFormConfig(id, config);
294
+ res.inputType = config.inputType || type;
295
+ res.placeholder = config.placeholder || (config.inputType == "mask" ? "_" : "");
296
+ res.step = config.step || 1;
297
+ res.mask = config.mask || null;
298
+ return new DynamicInputModel(res, layout);
299
+ }
300
+ function createFormSelect(id, config, layout) {
301
+ const res = createFormConfig(id, config);
302
+ res.options = config.options || [];
303
+ return new DynamicSelectModel(res, layout);
304
+ }
305
+ function createFormTextarea(id, config, layout) {
306
+ const res = createFormConfig(id, config);
307
+ res.cols = config.cols || 10;
308
+ res.rows = config.rows || 3;
309
+ res.wrap = config.wrap || "soft";
310
+ return new DynamicTextAreaModel(res, layout);
311
+ }
312
+ function createFormFile(id, config, layout) {
313
+ const res = createFormConfig(id, config);
314
+ res.accept = config.accept || ["jpg", "jpeg", "png"];
315
+ res.multiple = config.multiple || false;
316
+ res.url = ObjectUtils.isString(config.url) ? config.url : "assets";
317
+ return new DynamicFileUploadModel(res, layout);
318
+ }
319
+
320
+ function getFormComponent(...providers) {
321
+ const factory = cachedFactory(providers);
322
+ return (model, injector) => {
323
+ const customizers = factory(injector);
324
+ for (const customizer of customizers) {
325
+ const component = customizer.acceptModel(model) ? customizer.getFormComponent(model) : null;
326
+ if (component) {
327
+ return component;
328
+ }
329
+ }
330
+ return null;
331
+ };
332
+ }
333
+ function customizeFormModel(...providers) {
334
+ const factory = cachedFactory(providers);
335
+ return async (property, schema, model, config, injector) => {
336
+ const customizers = factory(injector);
337
+ for (const customizer of customizers) {
338
+ const accept = customizer.acceptModel(model);
339
+ if (accept) {
340
+ return customizer.customizeModel(model, config, property, schema);
341
+ }
342
+ }
343
+ return model;
344
+ };
345
+ }
346
+
347
+ function isStringWithVal(val) {
348
+ return typeof val == "string" && val.length > 0;
349
+ }
350
+ function findRefs(property) {
351
+ const refs = Array.isArray(property.allOf)
352
+ ? property.allOf.map(o => o.$ref).filter(isStringWithVal)
353
+ : [property.items?.$ref, property.$ref].filter(isStringWithVal);
354
+ return refs.map(t => t.split("/").pop());
355
+ }
356
+ function replaceSpecialChars(str, to = "-") {
357
+ return `${str}`.replace(/[&\/\\#, +()$~%.@'":*?<>{}]/g, to);
358
+ }
359
+ function mergeFormModels(formModels) {
360
+ const res = [];
361
+ for (const formModel of formModels) {
362
+ for (const subModel of formModel) {
363
+ const index = res.findIndex(t => t.id == subModel.id);
364
+ if (index >= 0) {
365
+ res[index] = subModel;
366
+ continue;
367
+ }
368
+ res.push(subModel);
369
+ }
370
+ }
371
+ return res;
372
+ }
373
+ function collectPathAble(start, getter) {
374
+ if (!start || !getter(start))
375
+ return [];
376
+ const parts = [];
377
+ let currentPath = start;
378
+ while (currentPath) {
379
+ const val = getter(currentPath);
380
+ if (val) {
381
+ parts.unshift(val);
382
+ }
383
+ currentPath = currentPath.parent;
384
+ }
385
+ return parts;
386
+ }
387
+ function getDynamicPath(start) {
388
+ return collectPathAble(start, t => t.id).join(".");
389
+ }
390
+ const MIN_INPUT_NUM = -999999999;
391
+ const MAX_INPUT_NUM = 999999999;
392
+ const EDITOR_FORMATS = ["php", "json", "html", "css", "scss"];
393
+
394
+ function validateJSON(control) {
395
+ const value = control.value;
396
+ if (!value)
397
+ return null;
398
+ try {
399
+ JSON.parse(value);
400
+ return null;
401
+ }
402
+ catch (e) {
403
+ return { json: true };
404
+ }
405
+ }
406
+ function validateRequiredTranslation(control) {
407
+ const value = control.value;
408
+ if (!value || value.length == 0)
409
+ return { requiredTranslation: true };
410
+ return value.findIndex(t => (t.lang == "de" || t.lang == "en") && !t.translation) < 0
411
+ ? null
412
+ : { requiredTranslation: true };
413
+ }
414
+ function validatePhone(control) {
415
+ const value = control.value;
416
+ if (!value)
417
+ return Promise.resolve(null);
418
+ const phoneRegexp = /^(?:\d){10,12}$/;
419
+ return phoneRegexp.test(value) ? null : { phone: true };
420
+ }
421
+ function validateItemsMinLength(minLength) {
422
+ return (control) => {
423
+ const value = control.value;
424
+ return (Array.isArray(value) && value.every(v => typeof v == "string" && v.length >= minLength))
425
+ ? null : { itemsMinLength: minLength };
426
+ };
427
+ }
428
+ function validateItemsMaxLength(maxLength) {
429
+ return (control) => {
430
+ const value = control.value;
431
+ return (Array.isArray(value) && value.every(v => typeof v == "string" && v.length <= maxLength))
432
+ ? null : { itemsMaxLength: maxLength };
433
+ };
434
+ }
435
+ function validateItemsMinValue(min) {
436
+ return (control) => {
437
+ const value = control.value;
438
+ return (Array.isArray(value) && value.every(v => typeof v == "number" && v >= min))
439
+ ? null : { itemsMinValue: min };
440
+ };
441
+ }
442
+ function validateItemsMaxValue(max) {
443
+ return (control) => {
444
+ const value = control.value;
445
+ return (Array.isArray(value) && value.every(v => typeof v == "number" && v <= max))
446
+ ? null : { itemsMaxValue: max };
447
+ };
448
+ }
449
+
450
+ const indexLabels = ["$ix", "$pix"];
451
+ class FormSubject extends Subject {
452
+ notifyCallback;
453
+ constructor(notifyCallback) {
454
+ super();
455
+ this.notifyCallback = notifyCallback;
456
+ }
457
+ handleNotifiedValue(controlModel, control, val) {
458
+ val.then(v => this.next(v));
459
+ }
460
+ notify(controlModel, control, root) {
461
+ const indexes = {};
462
+ let path = controlModel;
463
+ let ix = 0;
464
+ while (path) {
465
+ if (!isNaN(path.index)) {
466
+ const key = indexLabels[ix++] || `$pix${ix}`;
467
+ indexes[key] = path.index;
468
+ }
469
+ path = path.parent;
470
+ }
471
+ let value = this.notifyCallback(controlModel, control, root, indexes);
472
+ if (!(value instanceof Promise)) {
473
+ value = Promise.resolve(value);
474
+ }
475
+ this.handleNotifiedValue(controlModel, control, value);
476
+ }
477
+ }
478
+
479
+ class FormSelectSubject extends FormSubject {
480
+ handleNotifiedValue(controlModel, control, val) {
481
+ val.then(options => {
482
+ if (options.length == 0) {
483
+ this.next(options);
484
+ return;
485
+ }
486
+ const currentVal = control.value;
487
+ if (controlModel.multiple) {
488
+ const correctVal = (currentVal || []).filter(t => options.findIndex(o => o.value == t) >= 0);
489
+ if (correctVal.length !== currentVal?.length) {
490
+ control.setValue(correctVal, { onlySelf: true, emitEvent: false });
491
+ }
492
+ }
493
+ else {
494
+ const option = options.find(t => t.value == currentVal);
495
+ if (!option) {
496
+ control.setValue(controlModel.allowEmpty ? null : options[0]?.value ?? null, { onlySelf: true, emitEvent: false });
497
+ }
498
+ }
499
+ this.next(options);
500
+ });
501
+ }
502
+ }
503
+
504
+ function getFormValidationErrors(controls, parentPath = "") {
505
+ const errors = [];
506
+ Object.entries(controls).forEach(([name, control], ix) => {
507
+ const path = !parentPath ? name : `${parentPath}.${name}`;
508
+ if (control instanceof FormGroup) {
509
+ getFormValidationErrors(control.controls, path).forEach(error => errors.push(error));
510
+ return;
511
+ }
512
+ if (control instanceof FormArray) {
513
+ control.controls.forEach((control, ix) => {
514
+ getFormValidationErrors(control.controls, `${path}.${ix}`).forEach(error => errors.push(error));
515
+ });
516
+ return;
517
+ }
518
+ Object.entries(control.errors || {}).forEach(([errorKey, errorValue]) => {
519
+ errors.push({ control, path, errorKey, errorValue });
520
+ });
521
+ });
522
+ return errors;
523
+ }
524
+
525
+ class DynamicFormService extends DynamicFormService$1 {
526
+ openApi;
527
+ injector;
528
+ get api() {
529
+ return this.openApi.api;
530
+ }
531
+ get language() {
532
+ return this.api.language;
533
+ }
534
+ schemas;
535
+ constructor(cs, vs, openApi, injector) {
536
+ super(cs, vs);
537
+ this.openApi = openApi;
538
+ this.injector = injector;
539
+ }
540
+ patchGroup(value, formModel, formGroup) {
541
+ value = ObjectUtils.copy(value);
542
+ this.patchValueRecursive(value, formModel, formGroup);
543
+ formGroup.patchValue(value);
544
+ this.detectChanges();
545
+ }
546
+ patchForm(value, component) {
547
+ value = ObjectUtils.copy(value);
548
+ this.patchValueRecursive(value, component.model, component.group);
549
+ component.group.patchValue(value);
550
+ this.detectChanges(component);
551
+ }
552
+ serialize(formModel, formGroup) {
553
+ return this.serializeRecursive(formModel, formGroup);
554
+ }
555
+ notifyChanges(formModel, formGroup) {
556
+ this.notifyChangesRecursive(formModel, formGroup, formModel);
557
+ }
558
+ showErrors(form) {
559
+ this.showErrorsForGroup(form.group);
560
+ this.detectChanges(form);
561
+ }
562
+ getErrors(form) {
563
+ this.showErrors(form);
564
+ return new Promise(resolve => {
565
+ setTimeout(() => {
566
+ resolve(getFormValidationErrors(form.group.controls, ""));
567
+ }, 500);
568
+ });
569
+ }
570
+ patchValueRecursive(value, formModel, formGroup) {
571
+ if (!value)
572
+ return;
573
+ formModel?.forEach(subModel => {
574
+ const key = subModel.id;
575
+ const subValue = value[key];
576
+ const subControl = this.findControlByModel(subModel, formGroup);
577
+ if (subModel instanceof DynamicSelectModel && ObjectUtils.isObject(subValue)) {
578
+ value[key] = subValue.id || subValue._id || subValue;
579
+ return;
580
+ }
581
+ if (subModel instanceof DynamicDatePickerModel) {
582
+ value[key] = this.convertToDate(subValue);
583
+ return;
584
+ }
585
+ if (subModel instanceof DynamicFormArrayModel) {
586
+ const length = Array.isArray(subValue) ? subValue.length : 0;
587
+ const subArray = subControl;
588
+ while (subModel.size > length) {
589
+ this.removeFormArrayGroup(0, subArray, subModel);
590
+ }
591
+ while (subModel.size < length) {
592
+ this.insertFormArrayGroup(subModel.size, subArray, subModel);
593
+ }
594
+ for (let i = 0; i < length; i++) {
595
+ const itemModel = subModel.get(i);
596
+ this.patchValueRecursive(subValue[i], itemModel.group, subArray.at(i));
597
+ }
598
+ return;
599
+ }
600
+ if (subModel instanceof DynamicFormGroupModel) {
601
+ this.patchValueRecursive(subValue, subModel.group, subControl);
602
+ }
603
+ });
604
+ }
605
+ async serializeRecursive(formModel, formGroup) {
606
+ const result = {};
607
+ if (!formModel || !formGroup || !formGroup.value)
608
+ return result;
609
+ for (const i in formModel) {
610
+ const subModel = formModel[i];
611
+ const subControl = this.findControlByModel(subModel, formGroup);
612
+ const serializer = subModel.additional?.serializer;
613
+ if (ObjectUtils.isFunction(serializer)) {
614
+ result[subModel.id] = await serializer(subModel, subControl);
615
+ continue;
616
+ }
617
+ if (subModel.hidden && !subModel.additional?.serialize)
618
+ continue;
619
+ if (subModel instanceof DynamicFormArrayModel) {
620
+ const length = Array.isArray(subControl.value) ? subControl.value.length : 0;
621
+ const subArray = subControl;
622
+ const resArray = [];
623
+ for (let i = 0; i < length; i++) {
624
+ const itemModel = subModel.get(i);
625
+ resArray.push(await this.serializeRecursive(itemModel.group, subArray.at(i)));
626
+ }
627
+ result[subModel.id] = resArray;
628
+ continue;
629
+ }
630
+ if (subModel instanceof DynamicInputModel && !ObjectUtils.isNullOrUndefined(subControl.value)) {
631
+ result[subModel.id] = subModel.inputType == "number"
632
+ ? parseFloat((`${subControl.value}` || "0").replace(/,/gi, ".")) ?? null
633
+ : subControl.value;
634
+ continue;
635
+ }
636
+ if (subModel instanceof DynamicFormGroupModel) {
637
+ result[subModel.id] = await this.serializeRecursive(subModel.group, subControl);
638
+ continue;
639
+ }
640
+ result[subModel.id] = subControl.value;
641
+ }
642
+ return result;
643
+ }
644
+ notifyChangesRecursive(formModel, formGroup, root) {
645
+ if (!formModel || !formGroup)
646
+ return;
647
+ for (const i in formModel) {
648
+ const subModel = formModel[i];
649
+ const subControl = this.findControlByModel(subModel, formGroup);
650
+ if (subModel instanceof DynamicFormArrayModel) {
651
+ const length = Array.isArray(subControl.value) ? subControl.value.length : 0;
652
+ const subArray = subControl;
653
+ for (let i = 0; i < length; i++) {
654
+ const itemModel = subModel.get(i);
655
+ this.notifyChangesRecursive(itemModel.group, subArray.at(i), root);
656
+ }
657
+ continue;
658
+ }
659
+ if (subModel instanceof DynamicFormGroupModel) {
660
+ this.notifyChangesRecursive(subModel.group, subControl, root);
661
+ continue;
662
+ }
663
+ this.updateSelectOptions(subModel, subControl, root);
664
+ }
665
+ }
666
+ updateSelectOptions(formControlModel, formControl, root) {
667
+ if (formControlModel instanceof DynamicSelectModel) {
668
+ let options = formControlModel.options$;
669
+ if (options instanceof FormSubject) {
670
+ options.notify(formControlModel, formControl, root);
671
+ return;
672
+ }
673
+ while (options instanceof Subject && options.source) {
674
+ options = options.source;
675
+ if (options instanceof FormSubject) {
676
+ options.notify(formControlModel, formControl, root);
677
+ }
678
+ }
679
+ }
680
+ }
681
+ showErrorsForGroup(formGroup) {
682
+ if (!formGroup)
683
+ return;
684
+ formGroup.markAsTouched({ onlySelf: true });
685
+ const controls = Object.keys(formGroup.controls).map(id => formGroup.controls[id]);
686
+ this.showErrorsForControls(controls);
687
+ }
688
+ showErrorsForControls(controls) {
689
+ controls.forEach(control => {
690
+ if (control instanceof FormGroup) {
691
+ this.showErrorsForGroup(control);
692
+ return;
693
+ }
694
+ control.markAsTouched({ onlySelf: true });
695
+ if (control instanceof FormArray) {
696
+ this.showErrorsForControls(control.controls);
697
+ }
698
+ });
699
+ }
700
+ convertToDate(value) {
701
+ if (ObjectUtils.isNullOrUndefined(value))
702
+ return null;
703
+ const date = ObjectUtils.isDate(value)
704
+ ? value
705
+ : new Date(value);
706
+ return isNaN(date) ? new Date() : date;
707
+ }
708
+ async getFormModelForSchema(name, customizeModel) {
709
+ return (await this.getFormGroupModelForSchema(name, customizeModel)).group;
710
+ }
711
+ async getFormGroupModelForSchema(name, customizeModel) {
712
+ this.api.cache = {};
713
+ this.schemas = await this.openApi.getSchemas();
714
+ const fieldSets = [];
715
+ const customizeModels = async (property, schema, modelType, config) => {
716
+ const model = new modelType(config);
717
+ if (model instanceof DynamicFormValueControlModel) {
718
+ model.value = (model instanceof DynamicDatePickerModel)
719
+ ? this.convertToDate(property.default) : property.default;
720
+ }
721
+ if (!ObjectUtils.isFunction(customizeModel))
722
+ return [model];
723
+ let res = customizeModel(property, schema, model, config, this.injector);
724
+ if (!res)
725
+ return [model];
726
+ if (res instanceof Promise) {
727
+ res = await res;
728
+ }
729
+ return Array.isArray(res) ? res : [res];
730
+ };
731
+ const schema = this.schemas[name];
732
+ const controls = await this.getFormModelForSchemaDef(schema, fieldSets, customizeModels);
733
+ const idFields = [
734
+ createFormInput("id", { hidden: true }),
735
+ createFormInput("_id", { hidden: true })
736
+ ].filter(t => !controls.some(c => c.id == t.id));
737
+ const config = {
738
+ id: "root",
739
+ group: [
740
+ // -- Hidden id fields --
741
+ ...idFields,
742
+ // -- Main form controls --
743
+ ...controls
744
+ ],
745
+ fieldSets
746
+ };
747
+ const root = await customizeModels({
748
+ id: "root",
749
+ type: "object",
750
+ properties: schema.properties
751
+ }, schema, DynamicFormGroupModel, config);
752
+ // Check if the customized root wrapper returned an array
753
+ if (Array.isArray(root)) {
754
+ controls.length = 0;
755
+ for (const model of root) {
756
+ if (model instanceof DynamicFormGroupModel && model.id === "root") {
757
+ return model;
758
+ }
759
+ else {
760
+ controls.push(model);
761
+ }
762
+ }
763
+ }
764
+ return new DynamicFormGroupModel({
765
+ ...config,
766
+ group: controls
767
+ });
768
+ }
769
+ async getFormModelForSchemaDef(schema, fieldSets, customizeModels) {
770
+ if (!schema)
771
+ return [];
772
+ const keys = Object.keys(schema.properties || {});
773
+ const controls = [];
774
+ for (const p of keys) {
775
+ const property = schema.properties[p];
776
+ const fsName = property.hidden ? null : String(property.fieldSet || "");
777
+ if (fsName) {
778
+ const fs = fieldSets.find(t => t.id === fsName);
779
+ if (fs) {
780
+ fs.fields.push(p);
781
+ }
782
+ else {
783
+ fieldSets.push({ id: fsName, legend: `legend.${fsName}`, fields: [p] });
784
+ }
785
+ }
786
+ const models = await this.getFormControlModels(property, schema, customizeModels);
787
+ controls.push(...models);
788
+ }
789
+ return controls.filter(t => null !== t);
790
+ }
791
+ checkIsEditorProperty(property) {
792
+ if (!property.format)
793
+ return false;
794
+ return EDITOR_FORMATS.indexOf(property.format) >= 0 || property.format.endsWith("script");
795
+ }
796
+ async getFormControlModels(property, schema, customizeModels) {
797
+ const $enum = property.items?.enum || property.enum;
798
+ if (Array.isArray($enum) || isStringWithVal(property.optionsPath) || isStringWithVal(property.endpoint)) {
799
+ return customizeModels(property, schema, DynamicSelectModel, this.getFormSelectConfig(property, schema));
800
+ }
801
+ switch (property.type) {
802
+ case "string":
803
+ case "number":
804
+ case "integer":
805
+ case "textarea":
806
+ if (this.checkIsEditorProperty(property)) {
807
+ return customizeModels(property, schema, DynamicEditorModel, this.getFormEditorConfig(property, schema));
808
+ }
809
+ if (property.format == "textarea") {
810
+ return customizeModels(property, schema, DynamicTextAreaModel, this.getFormTextareaConfig(property, schema));
811
+ }
812
+ if (property.format == "date") {
813
+ return customizeModels(property, schema, DynamicDatePickerModel, this.getFormDatepickerConfig(property, schema));
814
+ }
815
+ return customizeModels(property, schema, DynamicInputModel, this.getFormInputConfig(property, schema));
816
+ case "object":
817
+ return customizeModels(property, schema, DynamicEditorModel, this.getFormEditorConfig(property, schema));
818
+ case "boolean":
819
+ return customizeModels(property, schema, DynamicCheckboxModel, this.getFormCheckboxConfig(property, schema));
820
+ case "array":
821
+ if (findRefs(property).length > 0) {
822
+ return customizeModels(property, schema, DynamicFormArrayModel, await this.getFormArrayConfig(property, schema, customizeModels));
823
+ }
824
+ else {
825
+ return customizeModels(property, schema, DynamicInputModel, this.getFormInputConfig(property, schema));
826
+ }
827
+ case "file":
828
+ return customizeModels(property, schema, DynamicFileUploadModel, this.getFormUploadConfig(property, schema));
829
+ }
830
+ if (findRefs(property).length > 0) {
831
+ return customizeModels(property, schema, DynamicFormGroupModel, await this.getFormGroupConfig(property, schema, customizeModels));
832
+ }
833
+ return [];
834
+ }
835
+ findModelByPath(parent, path) {
836
+ if (path.length == 0)
837
+ return parent;
838
+ const next = path.shift();
839
+ if (Array.isArray(parent)) {
840
+ return this.findModelByPath(parent.find(t => t.id == next), path);
841
+ }
842
+ if (parent instanceof DynamicFormGroupModel || parent instanceof DynamicFormArrayGroupModel) {
843
+ return this.findModelByPath(parent.group.find(t => t.id == next), path);
844
+ }
845
+ if (parent instanceof DynamicFormArrayModel) {
846
+ return this.findModelByPath(parent.groups.find(t => t.index == next), path);
847
+ }
848
+ return parent;
849
+ }
850
+ getFormControlConfig(property, schema) {
851
+ const validators = this.getValidators(property, schema);
852
+ const errorMessages = Object.keys(validators).reduce((res, key) => {
853
+ res[key] = `error.${key}`;
854
+ return res;
855
+ }, {});
856
+ return {
857
+ id: property.id,
858
+ label: ObjectUtils.isString(property.label) ? property.label : property.id,
859
+ hidden: property.hidden,
860
+ disabled: property.disabled,
861
+ validators,
862
+ errorMessages,
863
+ additional: Object.assign({}, property)
864
+ };
865
+ }
866
+ async getFormArrayConfig(property, schema, customizeModels) {
867
+ const fieldSets = [];
868
+ const subSchemas = findRefs(property).map(ref => this.schemas[ref]);
869
+ const subModels = await Promise.all(subSchemas.map(s => this.getFormModelForSchemaDef(s, fieldSets, customizeModels)));
870
+ return Object.assign(this.getFormControlConfig(property, schema), {
871
+ groupFactory: () => mergeFormModels(ObjectUtils.copy(subModels)),
872
+ initialCount: property.initialCount || 0,
873
+ sortable: property.sortable || false,
874
+ useTabs: property.useTabs || false,
875
+ addItem: property.addItem !== false,
876
+ insertItem: property.insertItem !== false,
877
+ cloneItem: property.cloneItem !== false,
878
+ moveItem: property.moveItem !== false,
879
+ removeItem: property.removeItem !== false,
880
+ clearItems: property.clearItems !== false
881
+ });
882
+ }
883
+ async getFormGroupConfig(property, schema, customizeModels) {
884
+ const fieldSets = [];
885
+ const subSchemas = findRefs(property).map(ref => this.schemas[ref]);
886
+ const subModels = await Promise.all(subSchemas.map(s => this.getFormModelForSchemaDef(s, fieldSets, customizeModels)));
887
+ return Object.assign(this.getFormControlConfig(property, schema), {
888
+ fieldSets,
889
+ group: mergeFormModels(subModels)
890
+ });
891
+ }
892
+ getFormInputConfig(property, schema) {
893
+ let inputType = StringUtils.has(property.id, "password", "Password") ? "password" : (property.format || property.items?.type || property.type);
894
+ switch (inputType) {
895
+ case "string":
896
+ inputType = "text";
897
+ break;
898
+ case "boolean":
899
+ inputType = "checkbox";
900
+ break;
901
+ case "textarea":
902
+ inputType = "textarea";
903
+ break;
904
+ case "integer":
905
+ inputType = "number";
906
+ break;
907
+ }
908
+ const sub = property.type == "array" ? property.items || property : property;
909
+ return Object.assign(this.getFormControlConfig(property, schema), {
910
+ inputType,
911
+ autoComplete: property.autoComplete || "off",
912
+ multiple: property.type == "array",
913
+ accept: ObjectUtils.isString(property.accept) ? property.accept : null,
914
+ mask: ObjectUtils.isString(property.mask) ? property.mask : null,
915
+ pattern: ObjectUtils.isString(property.pattern) ? property.pattern : null,
916
+ step: isNaN(sub.step) ? (isNaN(property.step) ? 1 : property.step) : sub.step,
917
+ min: isNaN(sub.minimum) ? MIN_INPUT_NUM : sub.minimum,
918
+ max: isNaN(sub.maximum) ? MAX_INPUT_NUM : sub.maximum,
919
+ minLength: isNaN(sub.minLength) ? 0 : sub.minLength,
920
+ maxLength: isNaN(sub.maxLength) ? MAX_INPUT_NUM : sub.maxLength,
921
+ placeholder: property.placeholder || ""
922
+ });
923
+ }
924
+ getFormEditorConfig(property, schema) {
925
+ const sub = property.type == "array" ? property.items || property : property;
926
+ return Object.assign(this.getFormControlConfig(property, schema), {
927
+ inputType: property.format || "json",
928
+ convertObject: property.type !== "string",
929
+ autoComplete: property.autoComplete || "off",
930
+ multiple: property.type == "array",
931
+ accept: ObjectUtils.isString(property.accept) ? property.accept : null,
932
+ mask: ObjectUtils.isString(property.mask) ? property.mask : null,
933
+ pattern: ObjectUtils.isString(property.pattern) ? property.pattern : null,
934
+ step: isNaN(sub.step) ? (isNaN(property.step) ? 1 : property.step) : sub.step,
935
+ minLength: isNaN(sub.minLength) ? 0 : sub.minLength,
936
+ maxLength: isNaN(sub.maxLength) ? MAX_INPUT_NUM : sub.maxLength,
937
+ placeholder: property.placeholder || ""
938
+ });
939
+ }
940
+ getFormTextareaConfig(property, schema) {
941
+ return Object.assign(this.getFormControlConfig(property, schema), {
942
+ cols: property.cols || null,
943
+ rows: property.rows || 10,
944
+ wrap: property.wrap || false,
945
+ autoComplete: property.autoComplete || "off",
946
+ multiple: property.type == "array",
947
+ minLength: isNaN(property.minLength) ? 0 : property.minLength,
948
+ maxLength: isNaN(property.maxLength) ? MAX_INPUT_NUM : property.maxLength,
949
+ placeholder: property.placeholder || ""
950
+ });
951
+ }
952
+ getFormDatepickerConfig(property, schema) {
953
+ return Object.assign(this.getFormControlConfig(property, schema), {
954
+ format: property.dateFormat || "dd.MM.yyyy",
955
+ min: this.convertToDate(property.min),
956
+ max: this.convertToDate(property.max),
957
+ });
958
+ }
959
+ getFormSelectOptions(property, schema) {
960
+ const $enum = property.items?.enum || property.enum;
961
+ if (Array.isArray($enum)) {
962
+ return new FormSelectSubject((selectModel, formControl) => {
963
+ const options = $enum.map(value => {
964
+ const label = property.translatable ? `${property.id}.${value}` : `${value}`;
965
+ return { value, label };
966
+ });
967
+ return this.fixSelectOptions(selectModel, formControl, options);
968
+ });
969
+ }
970
+ if (isStringWithVal(property.optionsPath)) {
971
+ return new FormSelectSubject(async (selectModel, control, root, indexes) => {
972
+ let path = property.optionsPath;
973
+ let target = control;
974
+ let model = selectModel;
975
+ if (path.startsWith("$root")) {
976
+ path = path.substring(5);
977
+ while (target.parent) {
978
+ target = target.parent;
979
+ }
980
+ model = root;
981
+ }
982
+ while (path.startsWith(".")) {
983
+ path = path.substring(1);
984
+ if (target.parent) {
985
+ target = target.parent;
986
+ }
987
+ model = model.parent || root;
988
+ }
989
+ Object.keys(indexes).forEach(key => {
990
+ path = path.replace(key, indexes[key]);
991
+ });
992
+ model = this.findModelByPath(model, path.split("."));
993
+ const modelOptions = model instanceof DynamicSelectModel
994
+ ? await firstValueFrom(model.options$) :
995
+ [];
996
+ const value = ObjectUtils.getValue(target.value, path);
997
+ const options = (!ObjectUtils.isArray(value) ? [] : value).map(value => {
998
+ const modelOption = modelOptions.find(t => t.value == value);
999
+ return { value, label: modelOption?.label || value };
1000
+ });
1001
+ return this.fixSelectOptions(selectModel, control, options);
1002
+ });
1003
+ }
1004
+ return new FormSelectSubject(async (selectModel, control) => {
1005
+ console.log(control.root?.value, "form select subject");
1006
+ const entries = Object.entries(control.root?.value || {});
1007
+ const endpoint = entries.reduce((res, [key, value]) => {
1008
+ return res.replace(new RegExp(`$${key}`, "gi"), `${value}`);
1009
+ }, `${property.endpoint}`);
1010
+ this.api.cache[endpoint] = this.api.cache[endpoint] || this.api.list(endpoint, this.api.makeListParams(1, -1)).then(result => {
1011
+ const items = ObjectUtils.isArray(result)
1012
+ ? result
1013
+ : (ObjectUtils.isArray(result.items) ? result.items : []);
1014
+ return items.map(i => {
1015
+ const item = ObjectUtils.isObject(i) ? i : { id: i };
1016
+ return {
1017
+ ...item,
1018
+ value: item.id || item._id,
1019
+ label: item[property.labelField] || item.label || item.id || item._id
1020
+ };
1021
+ });
1022
+ });
1023
+ const options = (await this.api.cache[endpoint]).map(t => Object.assign({}, t));
1024
+ return this.fixSelectOptions(selectModel, control, options);
1025
+ });
1026
+ }
1027
+ getFormSelectConfig(property, schema) {
1028
+ return Object.assign(this.getFormControlConfig(property, schema), {
1029
+ options: this.getFormSelectOptions(property, schema),
1030
+ multiple: property.type == "array",
1031
+ groupBy: property.groupBy,
1032
+ inline: property.inline,
1033
+ allowEmpty: property.allowEmpty,
1034
+ });
1035
+ }
1036
+ getFormUploadConfig(property, schema) {
1037
+ const url = this.api.url(property.url || "assets");
1038
+ const { accept, autoUpload, maxSize, minSize, removeUrl, showFileList } = property;
1039
+ return Object.assign(this.getFormControlConfig(property, schema), {
1040
+ url,
1041
+ accept,
1042
+ autoUpload,
1043
+ maxSize,
1044
+ minSize,
1045
+ removeUrl,
1046
+ showFileList
1047
+ });
1048
+ }
1049
+ getFormCheckboxConfig(property, schema) {
1050
+ return Object.assign(this.getFormControlConfig(property, schema), {
1051
+ indeterminate: property.indeterminate || false
1052
+ });
1053
+ }
1054
+ cloneFormArrayGroup(index, formArray, formArrayModel) {
1055
+ this.insertFormArrayGroup(index, formArray, formArrayModel);
1056
+ this.patchGroup(formArray.at(index + 1).value, formArrayModel.groups[index].group, formArray.at(index));
1057
+ formArrayModel.filterGroups();
1058
+ }
1059
+ async fixSelectOptions(model, control, options) {
1060
+ if (!options)
1061
+ return [];
1062
+ for (const option of options) {
1063
+ option.classes = [option.classes, model.getClasses(option, model, control, this.injector)].filter(isStringWithVal).join(" ");
1064
+ option.label = await this.language.getTranslation(option.label);
1065
+ }
1066
+ return options;
1067
+ }
1068
+ getValidators(property, schema) {
1069
+ const validators = {};
1070
+ if (ObjectUtils.isArray(schema.required) && schema.required.indexOf(property.id) >= 0) {
1071
+ validators.required = null;
1072
+ }
1073
+ this.addPropertyValidators(validators, property);
1074
+ this.addItemsValidators(validators, property.items);
1075
+ return validators;
1076
+ }
1077
+ addPropertyValidators(validators, property) {
1078
+ if (!property)
1079
+ return;
1080
+ if (!isNaN(property.minLength)) {
1081
+ validators.minLength = property.minLength;
1082
+ }
1083
+ if (!isNaN(property.maxLength)) {
1084
+ validators.maxLength = property.maxLength;
1085
+ }
1086
+ if (!isNaN(property.minimum)) {
1087
+ validators.min = property.minimum;
1088
+ }
1089
+ if (!isNaN(property.maximum)) {
1090
+ validators.max = property.maximum;
1091
+ }
1092
+ switch (property.format) {
1093
+ case "email":
1094
+ validators.email = null;
1095
+ break;
1096
+ }
1097
+ }
1098
+ addItemsValidators(validators, items) {
1099
+ if (!items)
1100
+ return;
1101
+ if (!isNaN(items.minLength)) {
1102
+ validators.itemsMinLength = items.minLength;
1103
+ }
1104
+ if (!isNaN(items.maxLength)) {
1105
+ validators.itemsMaxLength = items.maxLength;
1106
+ }
1107
+ if (!isNaN(items.minimum)) {
1108
+ validators.itemsMinValue = items.minimum;
1109
+ }
1110
+ if (!isNaN(items.maximum)) {
1111
+ validators.itemsMaxValue = items.maximum;
1112
+ }
1113
+ }
1114
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicFormService, deps: [{ token: i1.DynamicFormComponentService }, { token: i1.DynamicFormValidationService }, { token: OpenApiService }, { token: Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1115
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicFormService });
1116
+ }
1117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicFormService, decorators: [{
1118
+ type: Injectable
1119
+ }], ctorParameters: () => [{ type: i1.DynamicFormComponentService }, { type: i1.DynamicFormValidationService }, { type: i2.OpenApiService, decorators: [{
1120
+ type: Inject,
1121
+ args: [OpenApiService]
1122
+ }] }, { type: i0.Injector, decorators: [{
1123
+ type: Inject,
1124
+ args: [Injector]
1125
+ }] }] });
1126
+
1127
+ class AsyncSubmitDirective {
1128
+ toaster;
1129
+ cdr;
1130
+ elem;
1131
+ renderer;
1132
+ method;
1133
+ form;
1134
+ context;
1135
+ onSuccess;
1136
+ onError;
1137
+ loading;
1138
+ disabled;
1139
+ callback;
1140
+ onStatusChange;
1141
+ onSubmit;
1142
+ get isDisabled() {
1143
+ return this.disabled;
1144
+ }
1145
+ set isDisabled(value) {
1146
+ this.disabled = value;
1147
+ if (value) {
1148
+ this.renderer.setAttribute(this.elem.nativeElement, "disabled", "disabled");
1149
+ return;
1150
+ }
1151
+ this.renderer.removeAttribute(this.elem.nativeElement, "disabled");
1152
+ }
1153
+ get isLoading() {
1154
+ return this.loading;
1155
+ }
1156
+ constructor(toaster, cdr, elem, renderer) {
1157
+ this.toaster = toaster;
1158
+ this.cdr = cdr;
1159
+ this.elem = elem;
1160
+ this.renderer = renderer;
1161
+ this.onSuccess = new EventEmitter();
1162
+ this.onError = new EventEmitter();
1163
+ if (elem.nativeElement.tagName !== "BUTTON")
1164
+ return;
1165
+ renderer.setAttribute(elem.nativeElement, "type", "button");
1166
+ }
1167
+ ngOnInit() {
1168
+ if (!this.form)
1169
+ return;
1170
+ this.isDisabled = this.form.status !== "VALID";
1171
+ this.cdr.detectChanges();
1172
+ this.onStatusChange = this.form.onStatusChange.subscribe(() => {
1173
+ this.isDisabled = this.form.status !== "VALID";
1174
+ this.cdr.detectChanges();
1175
+ if (!this.callback || this.form.status == "PENDING")
1176
+ return;
1177
+ if (!this.disabled) {
1178
+ this.callback();
1179
+ }
1180
+ this.callback = null;
1181
+ });
1182
+ this.onSubmit = this.form.onSubmit.pipe(debounceTime(200)).subscribe(() => this.callMethod());
1183
+ }
1184
+ ngOnDestroy() {
1185
+ if (this.onStatusChange)
1186
+ this.onStatusChange.unsubscribe();
1187
+ if (this.onSubmit)
1188
+ this.onSubmit.unsubscribe();
1189
+ }
1190
+ click() {
1191
+ this.callback = () => this.callMethod();
1192
+ if (this.form.status === "INVALID") {
1193
+ console.log(getFormValidationErrors(this.form.group.controls));
1194
+ }
1195
+ if (this.form.status !== "VALID" && this.form.status !== "INVALID")
1196
+ return;
1197
+ this.callback();
1198
+ this.callback = null;
1199
+ }
1200
+ callMethod() {
1201
+ if (this.loading)
1202
+ return;
1203
+ this.loading = true;
1204
+ this.method(this.form, this.context).then(result => {
1205
+ this.loading = false;
1206
+ if (result) {
1207
+ this.onSuccess.emit(result);
1208
+ this.toaster.success(result.message, result.context);
1209
+ }
1210
+ }, reason => {
1211
+ if (!reason || !reason.message)
1212
+ throw new Error("Reason must implement IAsyncMessage interface");
1213
+ this.loading = false;
1214
+ this.onError.emit(reason);
1215
+ this.toaster.error(reason.message, reason.context);
1216
+ });
1217
+ }
1218
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AsyncSubmitDirective, deps: [{ token: TOASTER_SERVICE }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
1219
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.3", type: AsyncSubmitDirective, isStandalone: false, selector: "[async-submit]", inputs: { method: ["async-submit", "method"], form: "form", context: "context" }, outputs: { onSuccess: "onSuccess", onError: "onError" }, host: { listeners: { "click": "click()" }, properties: { "class.disabled": "this.isDisabled", "class.loading": "this.isLoading" } }, exportAs: ["async-submit"], ngImport: i0 });
1220
+ }
1221
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AsyncSubmitDirective, decorators: [{
1222
+ type: Directive,
1223
+ args: [{
1224
+ standalone: false,
1225
+ selector: "[async-submit]",
1226
+ exportAs: "async-submit"
1227
+ }]
1228
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1229
+ type: Inject,
1230
+ args: [TOASTER_SERVICE]
1231
+ }] }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { method: [{
1232
+ type: Input,
1233
+ args: ["async-submit"]
1234
+ }], form: [{
1235
+ type: Input
1236
+ }], context: [{
1237
+ type: Input
1238
+ }], onSuccess: [{
1239
+ type: Output
1240
+ }], onError: [{
1241
+ type: Output
1242
+ }], isDisabled: [{
1243
+ type: HostBinding,
1244
+ args: ["class.disabled"]
1245
+ }], isLoading: [{
1246
+ type: HostBinding,
1247
+ args: ["class.loading"]
1248
+ }], click: [{
1249
+ type: HostListener,
1250
+ args: ["click"]
1251
+ }] } });
1252
+
1253
+ class DynamicBaseFormComponent extends DynamicFormComponent {
1254
+ formService;
1255
+ events;
1256
+ group = null;
1257
+ groupModel = null;
1258
+ model = null;
1259
+ layout = null;
1260
+ labelPrefix = "label";
1261
+ getComponentType = null;
1262
+ blur = new EventEmitter();
1263
+ change = new EventEmitter();
1264
+ focus = new EventEmitter();
1265
+ contentTemplates = null;
1266
+ viewTemplates = null;
1267
+ get status() {
1268
+ return !this.group ? null : this.group.status;
1269
+ }
1270
+ onValueChange = new EventEmitter();
1271
+ onStatusChange = new EventEmitter();
1272
+ onSubmit = new EventEmitter();
1273
+ onDetectChanges = new EventEmitter();
1274
+ subscription = new Subscription();
1275
+ groupSubscription = new Subscription();
1276
+ constructor(formService, events, changeDetectorRef, componentService) {
1277
+ super(changeDetectorRef, componentService);
1278
+ this.formService = formService;
1279
+ this.events = events;
1280
+ this.getComponentType = () => null;
1281
+ }
1282
+ submit() {
1283
+ this.onSubmit.emit(this);
1284
+ }
1285
+ ngOnChanges(changes) {
1286
+ this.groupSubscription.unsubscribe();
1287
+ if (this.group) {
1288
+ this.groupSubscription = ObservableUtils.multiSubscription(this.group.statusChanges.subscribe(() => {
1289
+ this.onStatusChange.emit(this);
1290
+ }), this.group.valueChanges.pipe(debounceTime(500)).subscribe(() => {
1291
+ this.formService.notifyChanges(this.model, this.group);
1292
+ }), this.change.pipe(groupBy(ev => ev.model))
1293
+ .pipe(mergeMap(t => t.pipe(debounceTime(500))))
1294
+ .subscribe(ev => {
1295
+ this.onValueChange.emit({ ...ev, form: this });
1296
+ }));
1297
+ }
1298
+ if (changes.groupModel) {
1299
+ this.model = this.groupModel?.group;
1300
+ }
1301
+ if (changes.model) {
1302
+ this.groupModel = new DynamicFormGroupModel({ id: "root", group: this.model });
1303
+ }
1304
+ }
1305
+ ngAfterViewInit() {
1306
+ this.subscription = ObservableUtils.multiSubscription(ObservableUtils.subscribe({
1307
+ subjects: [this.contentTemplates.changes, this.viewTemplates.changes],
1308
+ cb: () => {
1309
+ const templates = this.contentTemplates.toArray().concat(this.viewTemplates.toArray());
1310
+ this.templates.reset(templates);
1311
+ }
1312
+ }), this.events.languageChanged.subscribe(() => {
1313
+ this.formService.notifyChanges(this.model, this.group);
1314
+ this.formService.detectChanges(this);
1315
+ }));
1316
+ }
1317
+ ngOnDestroy() {
1318
+ super.ngOnDestroy();
1319
+ this.subscription.unsubscribe();
1320
+ this.groupSubscription.unsubscribe();
1321
+ }
1322
+ detectChanges() {
1323
+ super.detectChanges();
1324
+ this.onDetectChanges.emit(this);
1325
+ }
1326
+ insertFormArrayGroup(index, formArray, formArrayModel) {
1327
+ this.formService.insertFormArrayGroup(index, formArray, formArrayModel);
1328
+ this.detectChanges();
1329
+ }
1330
+ cloneFormArrayGroup(index, formArray, formArrayModel) {
1331
+ this.formService.cloneFormArrayGroup(index, formArray, formArrayModel);
1332
+ this.detectChanges();
1333
+ }
1334
+ removeFormArrayGroup(index, formArray, formArrayModel) {
1335
+ this.formService.removeFormArrayGroup(index, formArray, formArrayModel);
1336
+ this.detectChanges();
1337
+ }
1338
+ moveFormArrayGroup(index, step, formArray, formArrayModel) {
1339
+ this.formService.moveFormArrayGroup(index, step, formArray, formArrayModel);
1340
+ this.detectChanges();
1341
+ }
1342
+ clearFormArray(formArray, formArrayModel) {
1343
+ this.formService.clearFormArray(formArray, formArrayModel);
1344
+ this.detectChanges();
1345
+ }
1346
+ getClass(model) {
1347
+ const parts = collectPathAble(model, p => p.id);
1348
+ if (parts.length == 0)
1349
+ return "";
1350
+ if (model instanceof DynamicFormGroupModel) {
1351
+ return `form-group-${parts.join("-")}`;
1352
+ }
1353
+ return `form-control-${parts.join("-")}`;
1354
+ }
1355
+ validate(showErrors = true) {
1356
+ if (!this.group)
1357
+ return Promise.resolve();
1358
+ return new Promise((resolve, reject) => {
1359
+ this.group.statusChanges.pipe(first(status => status == "VALID" || status == "INVALID")).subscribe(status => {
1360
+ if (showErrors) {
1361
+ this.formService.showErrors(this);
1362
+ }
1363
+ if (status == "VALID") {
1364
+ resolve(null);
1365
+ return;
1366
+ }
1367
+ reject(null);
1368
+ });
1369
+ this.group.updateValueAndValidity();
1370
+ });
1371
+ }
1372
+ async serialize(validate) {
1373
+ if (!this.group)
1374
+ return null;
1375
+ if (validate) {
1376
+ await this.validate();
1377
+ }
1378
+ return await this.formService.serialize(this.model, this.group);
1379
+ }
1380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormComponent, deps: [{ token: DynamicFormService }, { token: EventsService }, { token: i0.ChangeDetectorRef }, { token: i1.DynamicFormComponentService }], target: i0.ɵɵFactoryTarget.Component });
1381
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseFormComponent, isStandalone: false, selector: "dynamic-base-form", inputs: { group: "group", groupModel: "groupModel", model: "model", layout: "layout", labelPrefix: "labelPrefix", getComponentType: "getComponentType" }, outputs: { blur: "blur", change: "change", focus: "focus", onValueChange: "onValueChange", onStatusChange: "onStatusChange", onSubmit: "onSubmit", onDetectChanges: "onDetectChanges" }, queries: [{ propertyName: "contentTemplates", predicate: DynamicTemplateDirective }], viewQueries: [{ propertyName: "viewTemplates", predicate: DynamicTemplateDirective, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.Default });
1382
+ }
1383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormComponent, decorators: [{
1384
+ type: Component,
1385
+ args: [{
1386
+ standalone: false,
1387
+ selector: "dynamic-base-form",
1388
+ template: "",
1389
+ changeDetection: ChangeDetectionStrategy.Default
1390
+ }]
1391
+ }], ctorParameters: () => [{ type: DynamicFormService, decorators: [{
1392
+ type: Inject,
1393
+ args: [DynamicFormService]
1394
+ }] }, { type: i2.EventsService, decorators: [{
1395
+ type: Inject,
1396
+ args: [EventsService]
1397
+ }] }, { type: i0.ChangeDetectorRef }, { type: i1.DynamicFormComponentService }], propDecorators: { group: [{
1398
+ type: Input
1399
+ }], groupModel: [{
1400
+ type: Input
1401
+ }], model: [{
1402
+ type: Input
1403
+ }], layout: [{
1404
+ type: Input
1405
+ }], labelPrefix: [{
1406
+ type: Input
1407
+ }], getComponentType: [{
1408
+ type: Input
1409
+ }], blur: [{
1410
+ type: Output
1411
+ }], change: [{
1412
+ type: Output
1413
+ }], focus: [{
1414
+ type: Output
1415
+ }], contentTemplates: [{
1416
+ type: ContentChildren,
1417
+ args: [DynamicTemplateDirective]
1418
+ }], viewTemplates: [{
1419
+ type: ViewChildren,
1420
+ args: [DynamicTemplateDirective]
1421
+ }], onValueChange: [{
1422
+ type: Output
1423
+ }], onStatusChange: [{
1424
+ type: Output
1425
+ }], onSubmit: [{
1426
+ type: Output
1427
+ }], onDetectChanges: [{
1428
+ type: Output
1429
+ }] } });
1430
+
1431
+ class DynamicBaseFormControlContainerComponent extends DynamicFormControlContainerComponent {
1432
+ form;
1433
+ cdr;
1434
+ injector;
1435
+ contentTemplateList = null;
1436
+ klass = null;
1437
+ context = null;
1438
+ group = null;
1439
+ hostClass = null;
1440
+ inputTemplateList = null;
1441
+ layout = null;
1442
+ model = null;
1443
+ blur = new EventEmitter();
1444
+ change = new EventEmitter();
1445
+ focus = new EventEmitter();
1446
+ componentViewContainerRef = null;
1447
+ get componentType() {
1448
+ return this.form.getComponentType?.(this.model, this.injector)
1449
+ ?? this.componentService.getCustomComponentType(this.model);
1450
+ }
1451
+ get startTemplate() {
1452
+ return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
1453
+ ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_START")
1454
+ : this.layoutService.getStartTemplate(this.model, this.templates);
1455
+ }
1456
+ get endTemplate() {
1457
+ return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
1458
+ ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_END")
1459
+ : this.layoutService.getEndTemplate(this.model, this.templates);
1460
+ }
1461
+ get formService() {
1462
+ return this.form.formService;
1463
+ }
1464
+ onDetectChanges;
1465
+ constructor(form, cdr, injector, cfr, layoutService, validationService, componentService, relationService) {
1466
+ super(cdr, cfr, layoutService, validationService, componentService, relationService);
1467
+ this.form = form;
1468
+ this.cdr = cdr;
1469
+ this.injector = injector;
1470
+ }
1471
+ ngOnInit() {
1472
+ super.ngOnInit();
1473
+ this.onDetectChanges = this.form.onDetectChanges.subscribe(() => {
1474
+ this.changeDetectorRef.detectChanges();
1475
+ });
1476
+ }
1477
+ ngOnDestroy() {
1478
+ super.ngOnDestroy();
1479
+ this.onDetectChanges.unsubscribe();
1480
+ }
1481
+ getLabel() {
1482
+ const label = collectPathAble(this.model, p => p.label);
1483
+ if (label.length == 0)
1484
+ return "";
1485
+ if (this.form?.labelPrefix) {
1486
+ label.unshift(this.form.labelPrefix);
1487
+ }
1488
+ return label.join(".");
1489
+ }
1490
+ getLabelIcon() {
1491
+ if (this.context instanceof DynamicFormArrayGroupModel) {
1492
+ const arrayModel = this.context.context;
1493
+ if (arrayModel && arrayModel.sortBy == this.model.id) {
1494
+ return arrayModel.sortOrder;
1495
+ }
1496
+ }
1497
+ return null;
1498
+ }
1499
+ clickLabel() {
1500
+ if (this.context instanceof DynamicFormArrayGroupModel) {
1501
+ const arrayModel = this.context.context;
1502
+ if (arrayModel) {
1503
+ arrayModel.sortBy = this.model.id;
1504
+ }
1505
+ }
1506
+ }
1507
+ createFormControlComponent() {
1508
+ super.createFormControlComponent();
1509
+ const component = this.componentRef?.instance;
1510
+ if (!component || !ObjectUtils.isFunction(component.initialize))
1511
+ return;
1512
+ component.initialize(this.changeDetectorRef);
1513
+ }
1514
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormControlContainerComponent, deps: [{ token: DynamicBaseFormComponent }, { token: i0.ChangeDetectorRef }, { token: i0.Injector }, { token: i0.ComponentFactoryResolver }, { token: i1.DynamicFormLayoutService }, { token: i1.DynamicFormValidationService }, { token: i1.DynamicFormComponentService }, { token: i1.DynamicFormRelationService }], target: i0.ɵɵFactoryTarget.Component });
1515
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseFormControlContainerComponent, isStandalone: false, selector: "dynamic-base-form-control", inputs: { context: "context", group: "group", hostClass: "hostClass", inputTemplateList: ["templates", "inputTemplateList"], layout: "layout", model: "model" }, outputs: { blur: "blur", change: "change", focus: "focus" }, host: { properties: { "class": "this.klass" } }, providers: [
1516
+ { provide: DynamicFormControlContainerComponent, useExisting: DynamicBaseFormControlContainerComponent }
1517
+ ], queries: [{ propertyName: "contentTemplateList", predicate: DynamicTemplateDirective }], viewQueries: [{ propertyName: "componentViewContainerRef", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }], usesInheritance: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1518
+ }
1519
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormControlContainerComponent, decorators: [{
1520
+ type: Component,
1521
+ args: [{
1522
+ standalone: false,
1523
+ selector: "dynamic-base-form-control",
1524
+ template: "",
1525
+ changeDetection: ChangeDetectionStrategy.OnPush,
1526
+ providers: [
1527
+ { provide: DynamicFormControlContainerComponent, useExisting: DynamicBaseFormControlContainerComponent }
1528
+ ]
1529
+ }]
1530
+ }], ctorParameters: () => [{ type: DynamicBaseFormComponent }, { type: i0.ChangeDetectorRef }, { type: i0.Injector }, { type: i0.ComponentFactoryResolver }, { type: i1.DynamicFormLayoutService }, { type: i1.DynamicFormValidationService }, { type: i1.DynamicFormComponentService }, { type: i1.DynamicFormRelationService }], propDecorators: { contentTemplateList: [{
1531
+ type: ContentChildren,
1532
+ args: [DynamicTemplateDirective]
1533
+ }], klass: [{
1534
+ type: HostBinding,
1535
+ args: ["class"]
1536
+ }], context: [{
1537
+ type: Input
1538
+ }], group: [{
1539
+ type: Input
1540
+ }], hostClass: [{
1541
+ type: Input
1542
+ }], inputTemplateList: [{
1543
+ type: Input,
1544
+ args: ["templates"]
1545
+ }], layout: [{
1546
+ type: Input
1547
+ }], model: [{
1548
+ type: Input
1549
+ }], blur: [{
1550
+ type: Output
1551
+ }], change: [{
1552
+ type: Output
1553
+ }], focus: [{
1554
+ type: Output
1555
+ }], componentViewContainerRef: [{
1556
+ type: ViewChild,
1557
+ args: ["componentViewContainer", {
1558
+ read: ViewContainerRef,
1559
+ static: true
1560
+ }]
1561
+ }] } });
1562
+
1563
+ class DynamicBaseFormArrayComponent extends DynamicFormArrayComponent {
1564
+ form;
1565
+ injector;
1566
+ cdr;
1567
+ formLayout = null;
1568
+ group = null;
1569
+ layout = null;
1570
+ model = null;
1571
+ templates = null;
1572
+ blur = new EventEmitter();
1573
+ change = new EventEmitter();
1574
+ customEvent = new EventEmitter();
1575
+ focus = new EventEmitter();
1576
+ components = null;
1577
+ get useTabs() {
1578
+ return this.model?.useTabs;
1579
+ }
1580
+ subscription;
1581
+ constructor(layoutService, validationService, form, injector, cdr) {
1582
+ super(layoutService, validationService);
1583
+ this.form = form;
1584
+ this.injector = injector;
1585
+ this.cdr = cdr;
1586
+ }
1587
+ initialize(cdr) {
1588
+ this.subscription = this.model.filteredGroups.subscribe(filteredGroups => {
1589
+ this.updateGroups(filteredGroups);
1590
+ });
1591
+ this.model.initialize(this.array);
1592
+ }
1593
+ ngOnDestroy() {
1594
+ if (this.subscription)
1595
+ this.subscription.unsubscribe();
1596
+ }
1597
+ saveTab(index) {
1598
+ this.model.saveTab(index, this.model.getFiltered(index), this.model, this.injector);
1599
+ }
1600
+ restoreTab() {
1601
+ return this.model.restoreTab(this.model, this.injector);
1602
+ }
1603
+ getTabLabel(index, model) {
1604
+ return this.model.getTabLabel(index, model, this.model, this.array, this.injector);
1605
+ }
1606
+ getClass(context, place, model) {
1607
+ return [
1608
+ context == "element" ? this.getModelClass(model) : null,
1609
+ context == "element" ? this.getAdditionalClass(model) : null,
1610
+ super.getClass(context, place, model),
1611
+ model instanceof DynamicFormValueControlModel ? model.additional?.classes : null
1612
+ ].filter(cls => !!cls).join(" ");
1613
+ }
1614
+ getModelClass(model) {
1615
+ const parts = collectPathAble(model, p => p.id);
1616
+ if (parts.length == 0)
1617
+ return "";
1618
+ if (model instanceof DynamicFormGroupModel$1) {
1619
+ return `form-group-${parts.join("-")}`;
1620
+ }
1621
+ return `form-control-${parts.join("-")}`;
1622
+ }
1623
+ getAdditionalClass(model) {
1624
+ if (model instanceof DynamicFormArrayModel) {
1625
+ return model.additional?.classes;
1626
+ }
1627
+ if (model instanceof DynamicFormValueControlModel) {
1628
+ return model.additional?.classes;
1629
+ }
1630
+ return null;
1631
+ }
1632
+ updateGroups(filteredGroups) {
1633
+ this.cdr.detectChanges();
1634
+ this.components.forEach(t => t.cdr.detectChanges());
1635
+ }
1636
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormArrayComponent, deps: [{ token: i1.DynamicFormLayoutService }, { token: i1.DynamicFormValidationService }, { token: DynamicBaseFormComponent }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1637
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseFormArrayComponent, isStandalone: false, selector: "dynamic-base-form-array", inputs: { formLayout: "formLayout", group: "group", layout: "layout", model: "model", templates: "templates" }, outputs: { blur: "blur", change: "change", customEvent: "customEvent", focus: "focus" }, viewQueries: [{ propertyName: "components", predicate: i0.forwardRef(() => DynamicBaseFormControlContainerComponent), descendants: true }], usesInheritance: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1638
+ }
1639
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormArrayComponent, decorators: [{
1640
+ type: Component,
1641
+ args: [{
1642
+ standalone: false,
1643
+ selector: "dynamic-base-form-array",
1644
+ template: "",
1645
+ changeDetection: ChangeDetectionStrategy.OnPush
1646
+ }]
1647
+ }], ctorParameters: () => [{ type: i1.DynamicFormLayoutService }, { type: i1.DynamicFormValidationService }, { type: DynamicBaseFormComponent }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }], propDecorators: { formLayout: [{
1648
+ type: Input
1649
+ }], group: [{
1650
+ type: Input
1651
+ }], layout: [{
1652
+ type: Input
1653
+ }], model: [{
1654
+ type: Input
1655
+ }], templates: [{
1656
+ type: Input
1657
+ }], blur: [{
1658
+ type: Output
1659
+ }], change: [{
1660
+ type: Output
1661
+ }], customEvent: [{
1662
+ type: Output
1663
+ }], focus: [{
1664
+ type: Output
1665
+ }], components: [{
1666
+ type: ViewChildren,
1667
+ args: [forwardRef(() => DynamicBaseFormControlContainerComponent)]
1668
+ }] } });
1669
+
1670
+ class DynamicBaseFormControlComponent extends DynamicFormControlComponent {
1671
+ form;
1672
+ injector;
1673
+ cdr;
1674
+ formLayout = null;
1675
+ group = null;
1676
+ layout = null;
1677
+ model = null;
1678
+ blur = new EventEmitter();
1679
+ change = new EventEmitter();
1680
+ focus = new EventEmitter();
1681
+ subscription;
1682
+ constructor(layoutService, validationService, form, injector, cdr) {
1683
+ super(layoutService, validationService);
1684
+ this.form = form;
1685
+ this.injector = injector;
1686
+ this.cdr = cdr;
1687
+ }
1688
+ ngAfterViewInit() {
1689
+ this.subscription = this.control.valueChanges.pipe(debounceTime(500)).subscribe(value => {
1690
+ this.onValueChanged(value);
1691
+ });
1692
+ }
1693
+ ngOnDestroy() {
1694
+ if (!this.subscription)
1695
+ return;
1696
+ this.subscription.unsubscribe();
1697
+ }
1698
+ submit() {
1699
+ this.form.submit();
1700
+ }
1701
+ onValueChanged(value) {
1702
+ }
1703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormControlComponent, deps: [{ token: i1.DynamicFormLayoutService }, { token: i1.DynamicFormValidationService }, { token: DynamicBaseFormComponent }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1704
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseFormControlComponent, isStandalone: false, selector: "dynamic-base-form-control", inputs: { formLayout: "formLayout", group: "group", layout: "layout", model: "model" }, outputs: { blur: "blur", change: "change", focus: "focus" }, usesInheritance: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1705
+ }
1706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormControlComponent, decorators: [{
1707
+ type: Component,
1708
+ args: [{
1709
+ standalone: false,
1710
+ selector: "dynamic-base-form-control",
1711
+ template: "",
1712
+ changeDetection: ChangeDetectionStrategy.OnPush
1713
+ }]
1714
+ }], ctorParameters: () => [{ type: i1.DynamicFormLayoutService }, { type: i1.DynamicFormValidationService }, { type: DynamicBaseFormComponent }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }], propDecorators: { formLayout: [{
1715
+ type: Input
1716
+ }], group: [{
1717
+ type: Input
1718
+ }], layout: [{
1719
+ type: Input
1720
+ }], model: [{
1721
+ type: Input
1722
+ }], blur: [{
1723
+ type: Output
1724
+ }], change: [{
1725
+ type: Output
1726
+ }], focus: [{
1727
+ type: Output
1728
+ }] } });
1729
+
1730
+ class DynamicBaseFormGroupComponent extends DynamicFormGroupComponent {
1731
+ layoutService;
1732
+ validationService;
1733
+ formLayout = null;
1734
+ group = null;
1735
+ layout = null;
1736
+ model = null;
1737
+ templates = [];
1738
+ blur = new EventEmitter();
1739
+ change = new EventEmitter();
1740
+ customEvent = new EventEmitter();
1741
+ focus = new EventEmitter();
1742
+ components = null;
1743
+ constructor(layoutService, validationService) {
1744
+ super(layoutService, validationService);
1745
+ this.layoutService = layoutService;
1746
+ this.validationService = validationService;
1747
+ }
1748
+ getClass(context, place, model) {
1749
+ return [
1750
+ context == "element" ? this.getModelClass(model) : null,
1751
+ context == "element" ? this.getAdditionalClass(model) : null,
1752
+ super.getClass(context, place, model)
1753
+ ].filter(cls => !!cls).join(" ");
1754
+ }
1755
+ getModelClass(model) {
1756
+ const parts = collectPathAble(model, p => p.id);
1757
+ if (parts.length == 0)
1758
+ return "";
1759
+ if (model instanceof DynamicFormGroupModel) {
1760
+ return `form-group-${parts.join("-")}`;
1761
+ }
1762
+ return `form-control-${parts.join("-")}`;
1763
+ }
1764
+ getAdditionalClass(model) {
1765
+ if (model instanceof DynamicFormArrayModel) {
1766
+ return model.additional?.classes;
1767
+ }
1768
+ if (model instanceof DynamicFormValueControlModel) {
1769
+ return model.additional?.classes;
1770
+ }
1771
+ return null;
1772
+ }
1773
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormGroupComponent, deps: [{ token: i1.DynamicFormLayoutService }, { token: i1.DynamicFormValidationService }], target: i0.ɵɵFactoryTarget.Component });
1774
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseFormGroupComponent, isStandalone: false, selector: "dynamic-base-form-group", inputs: { formLayout: "formLayout", group: "group", layout: "layout", model: "model", templates: "templates" }, outputs: { blur: "blur", change: "change", customEvent: "customEvent", focus: "focus" }, viewQueries: [{ propertyName: "components", predicate: i0.forwardRef(() => DynamicFormControlContainerComponent), descendants: true }], usesInheritance: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1775
+ }
1776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseFormGroupComponent, decorators: [{
1777
+ type: Component,
1778
+ args: [{
1779
+ standalone: false,
1780
+ selector: "dynamic-base-form-group",
1781
+ template: "",
1782
+ changeDetection: ChangeDetectionStrategy.OnPush
1783
+ }]
1784
+ }], ctorParameters: () => [{ type: i1.DynamicFormLayoutService }, { type: i1.DynamicFormValidationService }], propDecorators: { formLayout: [{
1785
+ type: Input
1786
+ }], group: [{
1787
+ type: Input
1788
+ }], layout: [{
1789
+ type: Input
1790
+ }], model: [{
1791
+ type: Input
1792
+ }], templates: [{
1793
+ type: Input
1794
+ }], blur: [{
1795
+ type: Output
1796
+ }], change: [{
1797
+ type: Output
1798
+ }], customEvent: [{
1799
+ type: Output
1800
+ }], focus: [{
1801
+ type: Output
1802
+ }], components: [{
1803
+ type: ViewChildren,
1804
+ args: [forwardRef(() => DynamicFormControlContainerComponent)]
1805
+ }] } });
1806
+
1807
+ class DynamicBaseSelectComponent extends DynamicBaseFormControlComponent {
1808
+ groups$;
1809
+ hasOptions;
1810
+ ngOnInit() {
1811
+ this.groups$ = new BehaviorSubject([]);
1812
+ this.subscription = this.model.options$.subscribe(options => {
1813
+ const groupBy = this.model.inline || !this.model.multiple ? this.model.groupBy : null;
1814
+ const grouped = options.reduce((res, option) => {
1815
+ const key = replaceSpecialChars(groupBy ? option.props[this.model.groupBy] || "default" : "default", "-");
1816
+ res[key] = res[key] || [];
1817
+ res[key].push(option);
1818
+ return res;
1819
+ }, {});
1820
+ const groups = Object.keys(grouped).map(group => {
1821
+ return {
1822
+ group,
1823
+ options: grouped[group]
1824
+ };
1825
+ });
1826
+ this.hasOptions = groups.length > 0;
1827
+ this.groups$.next(groups);
1828
+ this.cdr.detectChanges();
1829
+ });
1830
+ }
1831
+ ngOnDestroy() {
1832
+ if (this.subscription)
1833
+ this.subscription.unsubscribe();
1834
+ }
1835
+ isSelected(option) {
1836
+ if (this.model.multiple) {
1837
+ return this.control.value?.indexOf(option.value) >= 0;
1838
+ }
1839
+ return this.control.value == option.value;
1840
+ }
1841
+ selectToggle(option, state) {
1842
+ if (this.model.multiple) {
1843
+ const value = Array.from(this.control.value || []);
1844
+ const index = value.indexOf(option.value);
1845
+ if (index >= 0) {
1846
+ value.splice(index, 1);
1847
+ }
1848
+ if (state) {
1849
+ value.push(option.value);
1850
+ }
1851
+ this.control.setValue(value);
1852
+ this.onChange(value);
1853
+ return;
1854
+ }
1855
+ this.control.setValue(option.value);
1856
+ this.onChange(option.value);
1857
+ }
1858
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1859
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.3", type: DynamicBaseSelectComponent, isStandalone: false, selector: "dynamic-base-select", usesInheritance: true, ngImport: i0, template: "", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1860
+ }
1861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicBaseSelectComponent, decorators: [{
1862
+ type: Component,
1863
+ args: [{
1864
+ standalone: false,
1865
+ selector: "dynamic-base-select",
1866
+ template: "",
1867
+ changeDetection: ChangeDetectionStrategy.OnPush
1868
+ }]
1869
+ }] });
1870
+
1871
+ // --- Components ---
1872
+ const components = [
1873
+ DynamicBaseFormComponent,
1874
+ DynamicBaseFormArrayComponent,
1875
+ DynamicBaseFormControlComponent,
1876
+ DynamicBaseFormControlContainerComponent,
1877
+ DynamicBaseFormGroupComponent,
1878
+ DynamicBaseSelectComponent
1879
+ ];
1880
+ // --- Directives ---
1881
+ const directives = [
1882
+ AsyncSubmitDirective,
1883
+ ];
1884
+ // --- Pipes ---
1885
+ const pipes = [];
1886
+ function defaultFormControlProvider() {
1887
+ return () => null;
1888
+ }
1889
+
1890
+ class NgxDynamicFormModule {
1891
+ static forRoot(config) {
1892
+ return {
1893
+ ngModule: NgxDynamicFormModule,
1894
+ providers: [
1895
+ DynamicFormService,
1896
+ {
1897
+ provide: DynamicFormService$1,
1898
+ useExisting: DynamicFormService
1899
+ },
1900
+ {
1901
+ provide: DYNAMIC_FORM_CONTROL_MAP_FN,
1902
+ useFactory: (config?.controlProvider || defaultFormControlProvider),
1903
+ deps: [Injector]
1904
+ }
1905
+ ]
1906
+ };
1907
+ }
1908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxDynamicFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1909
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.3", ngImport: i0, type: NgxDynamicFormModule, declarations: [DynamicBaseFormComponent, DynamicBaseFormArrayComponent, DynamicBaseFormControlComponent, DynamicBaseFormControlContainerComponent, DynamicBaseFormGroupComponent, DynamicBaseSelectComponent, AsyncSubmitDirective], imports: [CommonModule,
1910
+ FormsModule,
1911
+ ReactiveFormsModule,
1912
+ NgxUtilsModule], exports: [DynamicBaseFormComponent, DynamicBaseFormArrayComponent, DynamicBaseFormControlComponent, DynamicBaseFormControlContainerComponent, DynamicBaseFormGroupComponent, DynamicBaseSelectComponent, AsyncSubmitDirective, FormsModule,
1913
+ ReactiveFormsModule,
1914
+ NgxUtilsModule] });
1915
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxDynamicFormModule, providers: [
1916
+ ...pipes,
1917
+ { provide: NG_VALIDATORS, useValue: validateJSON, multi: true },
1918
+ { provide: NG_VALIDATORS, useValue: validateRequiredTranslation, multi: true },
1919
+ { provide: NG_VALIDATORS, useValue: validateItemsMinLength, multi: true },
1920
+ { provide: NG_VALIDATORS, useValue: validateItemsMaxLength, multi: true },
1921
+ { provide: NG_VALIDATORS, useValue: validateItemsMinValue, multi: true },
1922
+ { provide: NG_VALIDATORS, useValue: validateItemsMaxValue, multi: true },
1923
+ {
1924
+ provide: DYNAMIC_VALIDATORS,
1925
+ useValue: new Map([
1926
+ ["json", validateJSON],
1927
+ ["requiredTranslation", validateRequiredTranslation],
1928
+ ["phone", validatePhone],
1929
+ ["itemsMinLength", validateItemsMinLength],
1930
+ ["itemsMaxLength", validateItemsMaxLength],
1931
+ ["itemsMinValue", validateItemsMinValue],
1932
+ ["itemsMaxValue", validateItemsMaxValue],
1933
+ ])
1934
+ }
1935
+ ], imports: [CommonModule,
1936
+ FormsModule,
1937
+ ReactiveFormsModule,
1938
+ NgxUtilsModule, FormsModule,
1939
+ ReactiveFormsModule,
1940
+ NgxUtilsModule] });
1941
+ }
1942
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxDynamicFormModule, decorators: [{
1943
+ type: NgModule,
1944
+ args: [{
1945
+ declarations: [
1946
+ ...components,
1947
+ ...directives,
1948
+ ...pipes
1949
+ ],
1950
+ imports: [
1951
+ CommonModule,
1952
+ FormsModule,
1953
+ ReactiveFormsModule,
1954
+ NgxUtilsModule
1955
+ ],
1956
+ exports: [
1957
+ ...components,
1958
+ ...directives,
1959
+ ...pipes,
1960
+ FormsModule,
1961
+ ReactiveFormsModule,
1962
+ NgxUtilsModule
1963
+ ],
1964
+ providers: [
1965
+ ...pipes,
1966
+ { provide: NG_VALIDATORS, useValue: validateJSON, multi: true },
1967
+ { provide: NG_VALIDATORS, useValue: validateRequiredTranslation, multi: true },
1968
+ { provide: NG_VALIDATORS, useValue: validateItemsMinLength, multi: true },
1969
+ { provide: NG_VALIDATORS, useValue: validateItemsMaxLength, multi: true },
1970
+ { provide: NG_VALIDATORS, useValue: validateItemsMinValue, multi: true },
1971
+ { provide: NG_VALIDATORS, useValue: validateItemsMaxValue, multi: true },
1972
+ {
1973
+ provide: DYNAMIC_VALIDATORS,
1974
+ useValue: new Map([
1975
+ ["json", validateJSON],
1976
+ ["requiredTranslation", validateRequiredTranslation],
1977
+ ["phone", validatePhone],
1978
+ ["itemsMinLength", validateItemsMinLength],
1979
+ ["itemsMaxLength", validateItemsMaxLength],
1980
+ ["itemsMinValue", validateItemsMinValue],
1981
+ ["itemsMaxValue", validateItemsMaxValue],
1982
+ ])
1983
+ }
1984
+ ]
1985
+ }]
1986
+ }] });
1987
+
1988
+ /**
1989
+ * Generated bundle index. Do not edit.
1990
+ */
1991
+
1992
+ export { AsyncSubmitDirective, DynamicBaseFormArrayComponent, DynamicBaseFormComponent, DynamicBaseFormControlComponent, DynamicBaseFormControlContainerComponent, DynamicBaseFormGroupComponent, DynamicBaseSelectComponent, DynamicEditorModel, DynamicFormArrayGroupModel, DynamicFormArrayModel, DynamicFormGroupModel, DynamicFormOption, DynamicFormService, DynamicSelectModel, EDITOR_FORMATS, FormSelectSubject, FormSubject, MAX_INPUT_NUM, MIN_INPUT_NUM, NgxDynamicFormModule, collectPathAble, createFormArray, createFormCheckbox, createFormDate, createFormEditor, createFormFile, createFormGroup, createFormInput, createFormSelect, createFormTextarea, customizeFormModel, getDynamicPath, getFormComponent, mergeFormModels, replaceSpecialChars, validateItemsMaxLength, validateItemsMaxValue, validateItemsMinLength, validateItemsMinValue, validateJSON, validatePhone, validateRequiredTranslation };
1993
+ //# sourceMappingURL=stemy-ngx-dynamic-form.mjs.map