@stemy/ngx-dynamic-form 10.2.24 → 12.0.0

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 (60) hide show
  1. package/bundles/stemy-ngx-dynamic-form.umd.js +1781 -901
  2. package/bundles/stemy-ngx-dynamic-form.umd.js.map +1 -1
  3. package/esm2015/ngx-dynamic-form/common-types.js +463 -7
  4. package/esm2015/ngx-dynamic-form/components/base/dynamic-form-base.component.js +87 -0
  5. package/esm2015/ngx-dynamic-form/components/dynamic-form/dynamic-form.component.js +91 -0
  6. package/esm2015/ngx-dynamic-form/components/dynamic-form-file/dynamic-form-file.component.js +112 -0
  7. package/esm2015/ngx-dynamic-form/components/dynamic-form-group/dynamic-form-group.component.js +19 -0
  8. package/esm2015/ngx-dynamic-form/components/dynamic-form-input/dynamic-form-input.component.js +69 -0
  9. package/esm2015/ngx-dynamic-form/components/dynamic-form-model/dynamic-form-model.component.js +23 -0
  10. package/esm2015/ngx-dynamic-form/components/dynamic-form-select/dynamic-form-select.component.js +73 -0
  11. package/esm2015/ngx-dynamic-form/components/dynamic-form-static/dynamic-form-static.component.js +20 -0
  12. package/esm2015/ngx-dynamic-form/components/dynamic-forms/dynamic-forms.component.js +134 -0
  13. package/esm2015/ngx-dynamic-form/directives/async-submit.directive.js +6 -17
  14. package/esm2015/ngx-dynamic-form/directives/dynamic-form-control.directive.js +32 -0
  15. package/esm2015/ngx-dynamic-form/directives/dynamic-form-group.directive.js +40 -0
  16. package/esm2015/ngx-dynamic-form/directives/dynamic-form-template.directive.js +38 -0
  17. package/esm2015/ngx-dynamic-form/ngx-dynamic-form.module.js +36 -35
  18. package/esm2015/ngx-dynamic-form/services/dynamic-form.service.js +41 -385
  19. package/esm2015/ngx-dynamic-form/services/form-utilities.js +108 -0
  20. package/esm2015/ngx-dynamic-form/services/open-api.service.js +130 -0
  21. package/esm2015/public_api.js +15 -6
  22. package/esm2015/stemy-ngx-dynamic-form.js +4 -3
  23. package/fesm2015/stemy-ngx-dynamic-form.js +1357 -593
  24. package/fesm2015/stemy-ngx-dynamic-form.js.map +1 -1
  25. package/ngx-dynamic-form/common-types.d.ts +181 -27
  26. package/ngx-dynamic-form/components/base/dynamic-form-base.component.d.ts +44 -0
  27. package/ngx-dynamic-form/components/dynamic-form/dynamic-form.component.d.ts +21 -0
  28. package/ngx-dynamic-form/components/dynamic-form-file/dynamic-form-file.component.d.ts +16 -0
  29. package/ngx-dynamic-form/components/dynamic-form-group/dynamic-form-group.component.d.ts +6 -0
  30. package/ngx-dynamic-form/components/dynamic-form-input/dynamic-form-input.component.d.ts +12 -0
  31. package/ngx-dynamic-form/components/dynamic-form-model/dynamic-form-model.component.d.ts +6 -0
  32. package/ngx-dynamic-form/components/dynamic-form-select/dynamic-form-select.component.d.ts +9 -0
  33. package/ngx-dynamic-form/components/dynamic-form-static/dynamic-form-static.component.d.ts +5 -0
  34. package/ngx-dynamic-form/components/dynamic-forms/dynamic-forms.component.d.ts +26 -0
  35. package/ngx-dynamic-form/directives/async-submit.directive.d.ts +1 -5
  36. package/ngx-dynamic-form/directives/dynamic-form-control.directive.d.ts +12 -0
  37. package/ngx-dynamic-form/directives/dynamic-form-group.directive.d.ts +14 -0
  38. package/ngx-dynamic-form/directives/dynamic-form-template.directive.d.ts +16 -0
  39. package/ngx-dynamic-form/ngx-dynamic-form.module.d.ts +15 -5
  40. package/ngx-dynamic-form/services/dynamic-form.service.d.ts +11 -39
  41. package/ngx-dynamic-form/services/form-utilities.d.ts +19 -0
  42. package/ngx-dynamic-form/services/open-api.service.d.ts +37 -0
  43. package/package.json +16 -17
  44. package/public_api.d.ts +14 -5
  45. package/stemy-ngx-dynamic-form.d.ts +3 -2
  46. package/stemy-ngx-dynamic-form.metadata.json +1 -1
  47. package/bundles/stemy-ngx-dynamic-form.umd.min.js +0 -16
  48. package/bundles/stemy-ngx-dynamic-form.umd.min.js.map +0 -1
  49. package/esm2015/ngx-dynamic-form/components/base/dynamic-base-form-control-container.component.js +0 -91
  50. package/esm2015/ngx-dynamic-form/components/base/dynamic-base-form.component.js +0 -129
  51. package/esm2015/ngx-dynamic-form/services/dynamic-form-validation.service.js +0 -11
  52. package/esm2015/ngx-dynamic-form/utils/dynamic-form-array.model.js +0 -8
  53. package/esm2015/ngx-dynamic-form/utils/form-subject.js +0 -18
  54. package/esm2015/ngx-dynamic-form/utils/validators.js +0 -28
  55. package/ngx-dynamic-form/components/base/dynamic-base-form-control-container.component.d.ts +0 -37
  56. package/ngx-dynamic-form/components/base/dynamic-base-form.component.d.ts +0 -37
  57. package/ngx-dynamic-form/services/dynamic-form-validation.service.d.ts +0 -5
  58. package/ngx-dynamic-form/utils/dynamic-form-array.model.d.ts +0 -12
  59. package/ngx-dynamic-form/utils/form-subject.d.ts +0 -8
  60. package/ngx-dynamic-form/utils/validators.d.ts +0 -4
@@ -1,12 +1,447 @@
1
- import { ReflectUtils, ObjectUtils, UniqueUtils, StringUtils, OpenApiService, TOASTER_SERVICE, ObservableUtils, EventsService, NgxUtilsModule } from '@stemy/ngx-utils';
2
- import { __awaiter } from 'tslib';
3
- import { Subject, Subscription } from 'rxjs';
4
- import { EventEmitter, Injectable, Directive, Inject, ChangeDetectorRef, ElementRef, Renderer2, Input, Output, HostBinding, HostListener, QueryList, Component, ChangeDetectionStrategy, ContentChildren, ViewChildren, ViewChild, ComponentFactoryResolver, ViewContainerRef, NgModule } from '@angular/core';
5
- import { FormGroup, FormArray, NgForm, FormsModule, ReactiveFormsModule, NG_VALIDATORS } from '@angular/forms';
6
- import { DynamicFormArrayModel as DynamicFormArrayModel$1, DynamicFormService as DynamicFormService$1, DynamicSelectModel, DynamicFormGroupModel, DynamicInputModel, DynamicFileUploadModel, DynamicCheckboxModel, DynamicTextAreaModel, DynamicFormComponentService, DynamicFormValidationService as DynamicFormValidationService$1, DynamicFormComponent, DynamicTemplateDirective, DynamicFormControlContainerComponent, DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DynamicFormLayoutService, DynamicFormRelationService, DYNAMIC_VALIDATORS } from '@ng-dynamic-forms/core';
1
+ import { InjectionToken, Directive, HostBinding, Injectable, Inject, Type, ComponentFactoryResolver, Injector, EventEmitter, ChangeDetectorRef, ElementRef, Renderer2, Input, Output, HostListener, ViewContainerRef, TemplateRef, ContentChild, ContentChildren, Component, ViewChildren, ViewChild, NgModule } from '@angular/core';
2
+ import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
3
+ import { ObjectUtils, ReflectUtils, UniqueUtils, TimerUtils, FileUtils, FactoryDependencies, StringUtils, ApiService, TOASTER_SERVICE, ArrayUtils, LANGUAGE_SERVICE, NgxUtilsModule } from '@stemy/ngx-utils';
4
+ import { __decorate } from 'tslib';
5
+ import { DatePipe, CommonModule } from '@angular/common';
7
6
  import { first } from 'rxjs/operators';
8
- import { CommonModule } from '@angular/common';
9
7
 
8
+ const FORM_GROUP_TYPE = new InjectionToken("form-group-provider");
9
+ const FORM_CONTROL_PROVIDER = new InjectionToken("form-control-provider");
10
+ class FormControlComponent {
11
+ get form() {
12
+ return !this.control ? null : this.control.form;
13
+ }
14
+ get data() {
15
+ return (!this.control ? {} : this.control.data);
16
+ }
17
+ get value() {
18
+ return !this.control ? null : this.control.value;
19
+ }
20
+ get meta() {
21
+ return !this.control ? null : this.control.meta;
22
+ }
23
+ get inputClass() {
24
+ return true;
25
+ }
26
+ }
27
+ FormControlComponent.decorators = [
28
+ { type: Directive }
29
+ ];
30
+ FormControlComponent.propDecorators = {
31
+ inputClass: [{ type: HostBinding, args: ["class.form-input",] }]
32
+ };
33
+ function createValidator(control) {
34
+ const data = control.data || {};
35
+ const validators = [data.validator].concat(data.validators || []).filter(ObjectUtils.isDefined).map(v => {
36
+ return ReflectUtils.resolve(v, control.injector);
37
+ });
38
+ return (ctrl) => new Promise((resolve) => {
39
+ const control = ctrl;
40
+ control.shouldValidate().then(should => {
41
+ if (!should) {
42
+ resolve(null);
43
+ return;
44
+ }
45
+ const validate = validators.map(v => v(control));
46
+ const metaValidator = control.meta.validator;
47
+ if (ObjectUtils.isFunction(metaValidator)) {
48
+ validate.push(metaValidator(control));
49
+ }
50
+ Promise.all(validate).then(results => {
51
+ results = results.filter(error => ObjectUtils.isObject(error) || ObjectUtils.isString(error));
52
+ let result = null;
53
+ if (results.length > 0) {
54
+ result = {};
55
+ results.forEach(error => {
56
+ if (ObjectUtils.isString(error)) {
57
+ result[error] = {};
58
+ return;
59
+ }
60
+ result = Object.assign(result, error);
61
+ });
62
+ }
63
+ resolve(result);
64
+ });
65
+ });
66
+ });
67
+ }
68
+ class DynamicFormControlHelper {
69
+ constructor(form, control = null) {
70
+ this.form = form;
71
+ this.control = control;
72
+ this.formId = UniqueUtils.uuid();
73
+ this.meta = {};
74
+ this.hidden = false;
75
+ this.dummyData = {};
76
+ this.readonlyTester = this.createTester("readonly");
77
+ this.hideTester = this.createTester("hidden");
78
+ this.serializeTester = this.createTester("shouldSerialize", (control) => {
79
+ return Promise.resolve(control.visible);
80
+ });
81
+ this.validateTester = this.createTester("shouldValidate", (control) => {
82
+ return Promise.resolve(control.visible);
83
+ });
84
+ }
85
+ get id() {
86
+ return !this.control ? null : this.control.id;
87
+ }
88
+ get type() {
89
+ return !this.control ? "" : this.control.type;
90
+ }
91
+ get data() {
92
+ return !this.control ? this.dummyData : this.control.data || this.dummyData;
93
+ }
94
+ get visible() {
95
+ return !this.hidden;
96
+ }
97
+ get provider() {
98
+ return this.ctrlProvider;
99
+ }
100
+ load(control) {
101
+ return !this.ctrlProvider ? Promise.resolve(null) : this.ctrlProvider.loader(control);
102
+ }
103
+ check(control) {
104
+ return new Promise(resolve => {
105
+ this.hideTester(control).then(hide => {
106
+ this.hidden = hide;
107
+ this.readonlyTester(control).then(readonly => {
108
+ resolve(control.form.readonly || readonly);
109
+ });
110
+ });
111
+ });
112
+ }
113
+ shouldSerialize(control) {
114
+ return this.serializeTester(control);
115
+ }
116
+ shouldValidate(control) {
117
+ return this.validateTester(control);
118
+ }
119
+ findProvider(control) {
120
+ this.ctrlProvider = !this.control || !this.control.type ? null : this.form.findProvider(control);
121
+ }
122
+ createTester(test, defaultFunc = null) {
123
+ const tester = this.data[test]
124
+ ? ReflectUtils.resolve(this.data[test], this.form.injector)
125
+ : (defaultFunc || (() => Promise.resolve(false)));
126
+ return (control) => tester(control);
127
+ }
128
+ }
129
+ class DynamicFormGroup extends FormGroup {
130
+ constructor(form, control = null) {
131
+ super({}, { updateOn: ((!control || !control.data) ? null : control.data.updateOn) || form.updateOn || "blur" });
132
+ this.form = form;
133
+ this.mName = "";
134
+ this.mModel = {};
135
+ this.mControls = [];
136
+ this.mSerializers = [];
137
+ this.mFieldSets = {};
138
+ this.helper = new DynamicFormControlHelper(form, control);
139
+ this.helper.findProvider(this);
140
+ this.initialized = false;
141
+ this.loading = false;
142
+ this.changeTimer = TimerUtils.createTimeout();
143
+ this.statusChanges.subscribe(() => {
144
+ const root = this.form.root;
145
+ root.onStatusChange.emit(root);
146
+ });
147
+ this.setAsyncValidators(createValidator(this));
148
+ }
149
+ get id() {
150
+ return this.helper.id;
151
+ }
152
+ get type() {
153
+ return this.helper.type;
154
+ }
155
+ get data() {
156
+ return this.helper.data;
157
+ }
158
+ get visible() {
159
+ return this.helper.visible;
160
+ }
161
+ get meta() {
162
+ return this.helper.meta;
163
+ }
164
+ get injector() {
165
+ return this.form.injector;
166
+ }
167
+ get label() {
168
+ return this.data.label !== "" ? `${this.prefix}${this.data.label}` : "";
169
+ }
170
+ get provider() {
171
+ return this.helper.provider;
172
+ }
173
+ get model() {
174
+ return this.mModel;
175
+ }
176
+ get formId() {
177
+ return this.helper.formId;
178
+ }
179
+ get formControls() {
180
+ return this.mControls || [];
181
+ }
182
+ get formFields() {
183
+ return this.mFieldSets || {};
184
+ }
185
+ get prefix() {
186
+ return !this.name ? "" : `${this.name}.`;
187
+ }
188
+ get name() {
189
+ return this.mName;
190
+ }
191
+ get state() {
192
+ return (this.loading ? "LOADING" : this.status);
193
+ }
194
+ static createFormControls(group, controls) {
195
+ if (!controls && ObjectUtils.isObject(group.model)) {
196
+ const props = Object.keys(group.model);
197
+ controls = props.map(id => {
198
+ return getFormControl(group.model, id);
199
+ }).filter(ObjectUtils.isDefined);
200
+ }
201
+ if (!controls)
202
+ return [];
203
+ return controls.map(ctrl => {
204
+ if (ctrl.type == "model") {
205
+ const subGroup = new DynamicFormGroup(group.form, ctrl);
206
+ const model = group.model[ctrl.id] || {};
207
+ const data = subGroup.getData();
208
+ data.name = data.name || group.name;
209
+ subGroup.setup(model, data);
210
+ group.model[ctrl.id] = model;
211
+ group.addControl(subGroup.id, subGroup);
212
+ return subGroup;
213
+ }
214
+ return new DynamicFormControl(ctrl, group);
215
+ });
216
+ }
217
+ static createFormSerializers(group, serializers) {
218
+ if (!serializers && ObjectUtils.isObject(group.model)) {
219
+ const props = Object.keys(group.model);
220
+ serializers = props.reduce((result, id) => {
221
+ const serializer = getFormSerializer(group.model, id);
222
+ if (!serializer)
223
+ return result;
224
+ result[id] = serializer;
225
+ return result;
226
+ }, {});
227
+ }
228
+ if (!serializers)
229
+ return [];
230
+ return Object.keys(serializers).map(id => {
231
+ const serializer = serializers[id] || defaultSerializer;
232
+ return !serializer ? null : {
233
+ id: id,
234
+ func: ReflectUtils.resolve(serializer, group.injector)
235
+ };
236
+ });
237
+ }
238
+ getData() {
239
+ return this.data;
240
+ }
241
+ getControl(id) {
242
+ return this.get(id);
243
+ }
244
+ load() {
245
+ const promises = this.mControls.map(c => c.load());
246
+ promises.push(this.helper.load(this));
247
+ return Promise.all(promises);
248
+ }
249
+ check() {
250
+ return new Promise(resolve => {
251
+ this.helper.check(this).then(readonly => {
252
+ if (readonly)
253
+ this.disable({ emitEvent: false });
254
+ else
255
+ this.enable({ emitEvent: false });
256
+ const promises = this.mControls.map(c => c.check());
257
+ Promise.all(promises).then(resolve);
258
+ });
259
+ });
260
+ }
261
+ shouldSerialize() {
262
+ return this.helper.shouldSerialize(this);
263
+ }
264
+ shouldValidate() {
265
+ return this.helper.shouldValidate(this);
266
+ }
267
+ serialize() {
268
+ return new Promise((resolve) => {
269
+ const result = {};
270
+ const serializers = this.mSerializers.map(s => {
271
+ return new Promise(resolve => {
272
+ s.func(s.id, this).then(res => {
273
+ const ctrl = this.getControl(s.id);
274
+ const promise = !ctrl ? Promise.resolve(true) : ctrl.shouldSerialize();
275
+ promise.then(should => {
276
+ if (should)
277
+ result[s.id] = res;
278
+ resolve(null);
279
+ });
280
+ });
281
+ });
282
+ });
283
+ Promise.all(serializers).then(() => resolve(result));
284
+ });
285
+ }
286
+ onFocus() {
287
+ this.markAsUntouched({ onlySelf: true });
288
+ }
289
+ onBlur() {
290
+ this.markAsTouched({ onlySelf: true });
291
+ }
292
+ showErrors() {
293
+ this.markAsTouched({ onlySelf: true });
294
+ this.mControls.forEach(ctrl => ctrl.showErrors());
295
+ }
296
+ reloadControls() {
297
+ let callback = () => { };
298
+ if (this.initialized === false) {
299
+ this.initialized = true;
300
+ this.loading = true;
301
+ callback = () => {
302
+ this.loading = false;
303
+ // this.cdr.detectChanges();
304
+ const root = this.form.root;
305
+ root.onInit.emit(root);
306
+ root.onStatusChange.emit(root);
307
+ // https://github.com/angular/angular/issues/14542
308
+ const statusTimer = TimerUtils.createInterval();
309
+ statusTimer.set(() => {
310
+ if (this.status == "PENDING")
311
+ return;
312
+ statusTimer.clear();
313
+ root.onStatusChange.emit(root);
314
+ }, 50);
315
+ setTimeout(statusTimer.clear, 5000);
316
+ };
317
+ }
318
+ const promise = new Promise(resolve => {
319
+ this.load().then(() => this.check().then(resolve));
320
+ });
321
+ promise.then(callback, callback);
322
+ return promise;
323
+ }
324
+ setup(model, info) {
325
+ this.mName = info.name || "";
326
+ this.mModel = model;
327
+ this.mControls.forEach(ctrl => this.removeControl(ctrl.id));
328
+ this.mControls = DynamicFormGroup.createFormControls(this, info.controls);
329
+ this.mControls.forEach((ctrl) => this.addControl(ctrl.id, ctrl));
330
+ this.mSerializers = DynamicFormGroup.createFormSerializers(this, info.serializers);
331
+ this.mFieldSets = info.fieldSets ? info.fieldSets.reduce((result, fs) => {
332
+ result[fs.id] = fs;
333
+ return result;
334
+ }, {}) : getFormFieldSets(Object.getPrototypeOf(model).constructor);
335
+ }
336
+ updateModel(control) {
337
+ this.model[control.id] = control.value;
338
+ this.changeTimer.clear();
339
+ this.changeTimer.set(() => {
340
+ this.check().then(() => this.reloadControlsFrom(control, new Set()).then(() => {
341
+ this.form.root.onChange.emit(control);
342
+ }));
343
+ }, 250);
344
+ }
345
+ reloadControlsFrom(control, controls) {
346
+ const data = control.data;
347
+ if (!data || !data.reload)
348
+ return Promise.resolve(null);
349
+ const reload = ObjectUtils.isArray(data.reload) ? data.reload : [data.reload];
350
+ return Promise.all(reload.map(id => {
351
+ const nextControl = this.getControl(id);
352
+ if (!nextControl || controls.has(nextControl))
353
+ return Promise.resolve(null);
354
+ controls.add(nextControl);
355
+ return new Promise(resolve => {
356
+ nextControl.load().then(() => {
357
+ this.reloadControlsFrom(nextControl, controls).then(resolve);
358
+ });
359
+ });
360
+ }));
361
+ }
362
+ }
363
+ class DynamicFormControl extends FormControl {
364
+ constructor(control, group) {
365
+ super(group.model[control.id], { updateOn: control.data.updateOn || group.updateOn });
366
+ this.control = control;
367
+ this.group = group;
368
+ this.group.addControl(control.id, this);
369
+ this.helper = new DynamicFormControlHelper(this.form, control);
370
+ this.helper.findProvider(this);
371
+ this.valueChanges.subscribe(() => this.group.updateModel(this));
372
+ this.setAsyncValidators(createValidator(this));
373
+ }
374
+ get id() {
375
+ return this.helper.id;
376
+ }
377
+ get type() {
378
+ return this.helper.type;
379
+ }
380
+ get data() {
381
+ return this.helper.data;
382
+ }
383
+ get visible() {
384
+ return this.helper.visible;
385
+ }
386
+ get meta() {
387
+ return this.helper.meta;
388
+ }
389
+ get form() {
390
+ return this.group.form;
391
+ }
392
+ get injector() {
393
+ return this.form.injector;
394
+ }
395
+ get label() {
396
+ return this.data.label !== "" ? `${this.group.prefix}${this.data.label}` : "";
397
+ }
398
+ get provider() {
399
+ return this.helper.provider;
400
+ }
401
+ get model() {
402
+ return this.group.model;
403
+ }
404
+ get formId() {
405
+ return this.helper.formId;
406
+ }
407
+ getData() {
408
+ return this.data;
409
+ }
410
+ getControl(id) {
411
+ return null;
412
+ }
413
+ load() {
414
+ return this.helper.load(this);
415
+ }
416
+ check() {
417
+ const check = this.helper.check(this);
418
+ check.then(readonly => {
419
+ if (readonly || this.group.disabled)
420
+ this.disable({ emitEvent: false });
421
+ else
422
+ this.enable({ emitEvent: false });
423
+ });
424
+ return check;
425
+ }
426
+ shouldSerialize() {
427
+ return this.helper.shouldSerialize(this);
428
+ }
429
+ shouldValidate() {
430
+ return this.helper.shouldValidate(this);
431
+ }
432
+ serialize() {
433
+ return Promise.resolve(this.value);
434
+ }
435
+ onFocus() {
436
+ this.markAsUntouched({ onlySelf: true });
437
+ }
438
+ onBlur() {
439
+ this.markAsTouched({ onlySelf: true });
440
+ }
441
+ showErrors() {
442
+ this.markAsTouched({ onlySelf: true });
443
+ }
444
+ }
10
445
  // --- Decorator functions ---
11
446
  const emptyArray = [];
12
447
  const emptyTester = () => {
@@ -14,8 +449,8 @@ const emptyTester = () => {
14
449
  };
15
450
  const ɵ0 = emptyTester;
16
451
  function defaultSerializer(id, parent) {
17
- const control = parent.get(id);
18
- return !control ? null : control.value;
452
+ const control = parent.getControl(id);
453
+ return !control ? Promise.resolve(parent.model[id]) : control.serialize();
19
454
  }
20
455
  function FormSerializable(serializer) {
21
456
  return (target, propertyKey) => {
@@ -66,6 +501,24 @@ function FormFieldSet(data) {
66
501
  ReflectUtils.defineMetadata("dynamicFormFieldSets", sets, target);
67
502
  };
68
503
  }
504
+ function provideFormGroup(component) {
505
+ return {
506
+ provide: FORM_GROUP_TYPE,
507
+ useValue: component
508
+ };
509
+ }
510
+ function provideFormControl(component, acceptor, loader, priority = 0) {
511
+ return {
512
+ provide: FORM_CONTROL_PROVIDER,
513
+ multi: true,
514
+ useValue: {
515
+ component: component,
516
+ priority: priority,
517
+ acceptor: acceptor,
518
+ loader: loader
519
+ }
520
+ };
521
+ }
69
522
  function defineFormControl(target, propertyKey, control) {
70
523
  ReflectUtils.defineMetadata("dynamicFormControl", control, target, propertyKey);
71
524
  }
@@ -98,15 +551,14 @@ function createFormInput(id, data, type = "text") {
98
551
  data = control.data;
99
552
  data.type = data.type || type;
100
553
  data.classes = !data.classes ? `form-group-${data.type}` : `${data.classes} form-group-${data.type}`;
101
- data.placeholder = data.placeholder || (data.type == "mask" ? "_" : "");
554
+ data.placeholder = data.placeholder || "";
102
555
  data.step = data.step || 1;
103
- data.mask = data.mask || [/\w*/gi];
104
556
  return control;
105
557
  }
106
558
  function createFormSelect(id, data) {
107
559
  const control = createFormControl(id, "select", data);
108
560
  data = control.data;
109
- data.options = data.options || [];
561
+ data.options = data.options || (() => Promise.resolve([]));
110
562
  data.type = data.type || "select";
111
563
  const classType = data.type == "select" ? "select" : `select-${data.type}`;
112
564
  data.classes = !data.classes ? `form-group-${classType}` : `${data.classes} form-group-${classType}`;
@@ -139,447 +591,286 @@ function createFormFile(id, data) {
139
591
  return control;
140
592
  }
141
593
 
142
- function validateJSON(control) {
143
- const value = control.value;
144
- if (!value)
145
- return null;
146
- try {
147
- JSON.parse(value);
148
- return null;
149
- }
150
- catch (e) {
151
- return { json: true };
594
+ class DynamicFormService {
595
+ constructor(components, groupType, resolver, injector) {
596
+ this.components = components;
597
+ this.groupType = groupType;
598
+ this.resolver = resolver;
599
+ this.injector = injector;
600
+ }
601
+ findProvider(control) {
602
+ if (!control)
603
+ return null;
604
+ const providers = this.components.filter(p => p.acceptor(control));
605
+ if (providers.length == 0) {
606
+ throw new Error(`No component provider for control: ${JSON.stringify({
607
+ id: control.id,
608
+ type: control.type,
609
+ data: control.data
610
+ })}`);
611
+ }
612
+ // Sort providers
613
+ providers.sort((a, b) => ObjectUtils.compare(a.priority, b.priority));
614
+ return providers[0];
615
+ }
616
+ createComponent(vcr, provider) {
617
+ vcr.clear();
618
+ if (!provider)
619
+ return null;
620
+ const factory = this.resolver.resolveComponentFactory(provider.component);
621
+ return vcr.createComponent(factory).instance;
622
+ }
623
+ createGroup(vcr) {
624
+ vcr.clear();
625
+ const factory = this.resolver.resolveComponentFactory(this.groupType);
626
+ return vcr.createComponent(factory).instance;
152
627
  }
153
628
  }
154
- function validateRequiredTranslation(control) {
155
- const value = control.value;
156
- if (!value || value.length == 0)
157
- return { requiredTranslation: true };
158
- return value.findIndex(t => (t.lang == "de" || t.lang == "en") && !t.translation) < 0
159
- ? null
160
- : { requiredTranslation: true };
161
- }
162
- function validatePhone(control) {
163
- const value = control.value;
164
- if (!value)
165
- return Promise.resolve(null);
166
- const phoneRegexp = /^(?:\d){10,12}$/;
167
- return phoneRegexp.test(value) ? null : { phone: true };
168
- }
629
+ DynamicFormService.decorators = [
630
+ { type: Injectable }
631
+ ];
632
+ DynamicFormService.ctorParameters = () => [
633
+ { type: Array, decorators: [{ type: Inject, args: [FORM_CONTROL_PROVIDER,] }] },
634
+ { type: Type, decorators: [{ type: Inject, args: [FORM_GROUP_TYPE,] }] },
635
+ { type: ComponentFactoryResolver },
636
+ { type: Injector }
637
+ ];
169
638
 
170
- class FormSubject extends Subject {
171
- constructor(notifyCallback) {
172
- super();
173
- this.notifyCallback = notifyCallback;
639
+ class FormUtilities {
640
+ static checkField(expression = `true`) {
641
+ // @dynamic
642
+ const lambda = (control) => {
643
+ return Promise.resolve(ObjectUtils.evaluate(expression, { control: control }));
644
+ };
645
+ return lambda;
174
646
  }
175
- notify(controlModel, control) {
176
- return __awaiter(this, void 0, void 0, function* () {
177
- let value = this.notifyCallback(controlModel, control);
178
- if (value instanceof Promise) {
179
- value = yield value;
180
- }
181
- this.next(value);
182
- });
647
+ static checkReadonly(control) {
648
+ return Promise.resolve(control.visible && !control.disabled);
183
649
  }
184
- }
185
-
186
- class DynamicFormArrayModel extends DynamicFormArrayModel$1 {
187
- constructor(config, layout) {
188
- super(config, layout);
189
- this.additional = config.additional || {};
650
+ static readonly() {
651
+ return Promise.resolve(true);
190
652
  }
191
- }
192
-
193
- class DynamicFormService extends DynamicFormService$1 {
194
- constructor(cs, vs, openApi) {
195
- super(cs, vs);
196
- this.openApi = openApi;
197
- this.onDetectChanges = new EventEmitter();
198
- }
199
- get api() {
200
- return this.openApi.api;
201
- }
202
- get language() {
203
- return this.api.language;
204
- }
205
- patchGroup(value, formModel, formGroup) {
206
- this.patchValueRecursive(value, formModel, formGroup);
207
- this.detectChanges();
208
- formGroup.patchValue(ObjectUtils.copy(value));
209
- }
210
- patchForm(value, component) {
211
- this.patchValueRecursive(value, component.model, component.group);
212
- this.detectChanges(component);
213
- component.group.patchValue(value);
214
- }
215
- serialize(formModel, formGroup) {
216
- return this.serializeRecursive(formModel, formGroup);
217
- }
218
- notifyChanges(formModel, formGroup) {
219
- this.notifyChangesRecursive(formModel, formGroup);
220
- }
221
- updateSelectOptions(formControlModel, formControl) {
222
- if (formControlModel instanceof DynamicSelectModel) {
223
- let options = formControlModel.options$;
224
- if (options instanceof FormSubject) {
225
- options.notify(formControlModel, formControl);
226
- return;
227
- }
228
- while (options instanceof Subject && options.source) {
229
- options = options.source;
230
- if (options instanceof FormSubject) {
231
- options.notify(formControlModel, formControl);
232
- }
233
- }
653
+ static validateJSON(control) {
654
+ const value = control.value;
655
+ if (!value)
656
+ return Promise.resolve(null);
657
+ try {
658
+ JSON.parse(value);
659
+ return Promise.resolve(null);
660
+ }
661
+ catch (e) {
662
+ return Promise.resolve("error.not-valid-json");
234
663
  }
235
664
  }
236
- showErrors(form) {
237
- this.showErrorsForGroup(form.group);
238
- this.detectChanges(form);
239
- }
240
- detectChanges(formComponent) {
241
- super.detectChanges(formComponent);
242
- this.onDetectChanges.emit(formComponent);
243
- }
244
- patchValueRecursive(value, formModel, formGroup) {
245
- Object.keys(value).forEach(key => {
246
- const subModel = this.findModelById(key, formModel);
247
- const subValue = value[key];
248
- if (!subModel)
249
- return;
250
- const subControl = this.findControlByModel(subModel, formGroup);
251
- if (subModel instanceof DynamicSelectModel && ObjectUtils.isObject(subValue)) {
252
- value[key] = subValue.id || subValue._id || subValue;
253
- return;
254
- }
255
- if (subModel instanceof DynamicFormArrayModel$1) {
256
- const length = Array.isArray(subValue) ? subValue.length : 0;
257
- const subArray = subControl;
258
- while (subModel.size > length) {
259
- this.removeFormArrayGroup(0, subArray, subModel);
260
- }
261
- while (subModel.size < length) {
262
- this.insertFormArrayGroup(subModel.size, subArray, subModel);
263
- }
264
- for (let i = 0; i < length; i++) {
265
- const itemModel = subModel.get(i);
266
- this.patchValueRecursive(subValue[i], itemModel.group, subArray.at(i));
267
- }
268
- return;
269
- }
270
- if (subModel instanceof DynamicFormGroupModel) {
271
- this.patchValueRecursive(subValue, subModel.group, subControl);
272
- }
273
- });
274
- }
275
- serializeRecursive(formModel, formGroup) {
276
- var _a, _b;
277
- return __awaiter(this, void 0, void 0, function* () {
278
- const result = {};
279
- if (!formModel || !formGroup || !formGroup.value)
280
- return result;
281
- for (const i in formModel) {
282
- const subModel = formModel[i];
283
- const subControl = this.findControlByModel(subModel, formGroup);
284
- const serializer = (_a = subModel.additional) === null || _a === void 0 ? void 0 : _a.serializer;
285
- if (ObjectUtils.isFunction(serializer)) {
286
- result[subModel.id] = yield serializer(subModel, subControl);
287
- continue;
288
- }
289
- if (subModel instanceof DynamicFormArrayModel$1) {
290
- const length = Array.isArray(subControl.value) ? subControl.value.length : 0;
291
- const subArray = subControl;
292
- const resArray = [];
293
- for (let i = 0; i < length; i++) {
294
- const itemModel = subModel.get(i);
295
- resArray.push(yield this.serializeRecursive(itemModel.group, subArray.at(i)));
296
- }
297
- result[subModel.id] = resArray;
298
- continue;
299
- }
300
- if (subModel instanceof DynamicFormGroupModel) {
301
- result[subModel.id] = yield this.serializeRecursive(subModel.group, subControl);
302
- continue;
303
- }
304
- if (subModel instanceof DynamicInputModel && !ObjectUtils.isNullOrUndefined(subControl.value)) {
305
- result[subModel.id] = subModel.inputType == "number"
306
- ? (_b = parseFloat((`${subControl.value}` || "0").replace(/,/gi, "."))) !== null && _b !== void 0 ? _b : null : subControl.value;
307
- continue;
308
- }
309
- result[subModel.id] = subControl.value;
310
- }
311
- return result;
312
- });
665
+ static validateRequired(control) {
666
+ const value = control.value;
667
+ return Promise.resolve(!ObjectUtils.isNumber(value) && !value ? "error.required" : null);
313
668
  }
314
- notifyChangesRecursive(formModel, formGroup) {
315
- if (!formModel || !formGroup)
316
- return;
317
- for (const i in formModel) {
318
- const subModel = formModel[i];
319
- const subControl = this.findControlByModel(subModel, formGroup);
320
- if (subModel instanceof DynamicFormArrayModel$1) {
321
- const length = Array.isArray(subControl.value) ? subControl.value.length : 0;
322
- const subArray = subControl;
323
- for (let i = 0; i < length; i++) {
324
- const itemModel = subModel.get(i);
325
- this.notifyChangesRecursive(itemModel.group, subArray.at(i));
326
- }
327
- continue;
669
+ static validateMinLength(length) {
670
+ // @dynamic
671
+ const lambda = (control) => {
672
+ const value = control.value;
673
+ if (!ObjectUtils.isString(value) || value.length < length) {
674
+ return Promise.resolve({ "error.min-length": { length: length } });
328
675
  }
329
- if (subModel instanceof DynamicFormGroupModel) {
330
- this.notifyChangesRecursive(subModel.group, subControl);
331
- continue;
676
+ return Promise.resolve(null);
677
+ };
678
+ return lambda;
679
+ }
680
+ static validateMaxLength(length) {
681
+ // @dynamic
682
+ const lambda = (control) => {
683
+ const value = control.value;
684
+ if (!ObjectUtils.isString(value) || value.length > length) {
685
+ return Promise.resolve({ "error.max-length": { length: length } });
332
686
  }
333
- this.updateSelectOptions(subModel, subControl);
334
- }
687
+ return Promise.resolve(null);
688
+ };
689
+ return lambda;
690
+ }
691
+ static validateRequiredTranslation(control) {
692
+ const value = control.value;
693
+ if (!value || value.length == 0)
694
+ return Promise.resolve("error.required");
695
+ return Promise.resolve(value.findIndex(t => (t.lang == "de" || t.lang == "en") && !t.translation) < 0 ? null : "error.required");
696
+ }
697
+ static validateEmail(control) {
698
+ const value = control.value;
699
+ if (!value)
700
+ return Promise.resolve(null);
701
+ const emailRegexp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
702
+ return Promise.resolve(emailRegexp.test(value) ? null : "error.invalid-email");
703
+ }
704
+ static validatePhone(control) {
705
+ const value = control.value;
706
+ if (!value)
707
+ return Promise.resolve(null);
708
+ const phoneRegexp = /^(?:\d){10,12}$/;
709
+ return Promise.resolve(phoneRegexp.test(value) ? null : "error.invalid-phone");
710
+ }
711
+ static serializeLogo(id, parent) {
712
+ const value = parent.model[id];
713
+ return Promise.resolve(!value || value.length == 0 ? null : value);
714
+ }
715
+ static serializeFile(id, parent) {
716
+ const value = parent.model[id];
717
+ if (ObjectUtils.isBlob(value))
718
+ return Promise.resolve(value);
719
+ return Promise.resolve(!ObjectUtils.isString(value) || !value.startsWith("data:") ? null : FileUtils.dataToBlob(value));
720
+ }
721
+ static serializeNumber(id, parent) {
722
+ const value = parent.model[id];
723
+ return Promise.resolve(!value || value.length == 0 ? 0 : parseFloat(value));
724
+ }
725
+ static serializeJSON(id, parent) {
726
+ const value = parent.model[id];
727
+ return Promise.resolve(!value ? {} : JSON.parse(value));
728
+ }
729
+ static serializeDate(date, format = "yyyy-MM-dd", defaultValue = "") {
730
+ // @dynamic
731
+ const lambda = (id, parent) => {
732
+ const value = parent.model[id];
733
+ if (!ObjectUtils.isDate(value))
734
+ return Promise.resolve(value || defaultValue);
735
+ return Promise.resolve(!format ? value : date.transform(value, format));
736
+ };
737
+ return lambda;
335
738
  }
336
- showErrorsForGroup(formGroup) {
337
- if (!formGroup)
338
- return;
339
- formGroup.markAsTouched({ onlySelf: true });
340
- const controls = Object.keys(formGroup.controls).map(id => formGroup.controls[id]);
341
- this.showErrorsForControls(controls);
342
- }
343
- showErrorsForControls(controls) {
344
- controls.forEach(control => {
345
- if (control instanceof FormGroup) {
346
- this.showErrorsForGroup(control);
347
- return;
348
- }
349
- control.markAsTouched({ onlySelf: true });
350
- if (control instanceof FormArray) {
351
- this.showErrorsForControls(control.controls);
352
- }
353
- });
739
+ }
740
+ __decorate([
741
+ FactoryDependencies(DatePipe)
742
+ ], FormUtilities, "serializeDate", null);
743
+
744
+ class OpenApiService {
745
+ constructor(api) {
746
+ this.api = api;
747
+ const baseUrl = this.api.url("").replace("api/", "api-docs");
748
+ this.schemas = {};
749
+ this.schemasPromise = new Promise((resolve => {
750
+ this.api.client.get(baseUrl).subscribe((res) => {
751
+ this.schemas = res.definitions || {};
752
+ resolve(this.schemas);
753
+ }, () => {
754
+ resolve({});
755
+ });
756
+ }));
354
757
  }
355
- getFormModelForSchema(name) {
356
- return __awaiter(this, void 0, void 0, function* () {
357
- this.api.cache = {};
358
- this.schemas = yield this.openApi.getSchemas();
359
- return this.getFormModelForSchemaDef(this.schemas[name]);
758
+ getFormInputsForSchema(name) {
759
+ this.api.cache = {};
760
+ return this.schemasPromise.then(schemas => {
761
+ const schema = schemas[name];
762
+ const inputs = this.getFormInputsForSchemaDef(schema);
763
+ return inputs;
360
764
  });
361
765
  }
362
- getFormModelForSchemaDef(schema) {
766
+ getFormInputsForSchemaDef(schema) {
767
+ const inputs = {};
363
768
  if (!schema)
364
- return [];
365
- return Object.keys(schema.properties || {}).map(p => {
769
+ return inputs;
770
+ inputs.schema = schema;
771
+ inputs.serializers = {};
772
+ inputs.controls = Object.keys(schema.properties || {}).map(p => {
366
773
  const property = schema.properties[p];
367
- return this.getFormControlModel(property, schema);
774
+ property.id = p;
775
+ inputs.serializers[p] = (id, parent) => {
776
+ return Promise.resolve(parent.model[id]);
777
+ };
778
+ return this.getFormControlForProperty(property, schema);
368
779
  }).filter(t => null !== t);
780
+ return inputs;
369
781
  }
370
- getFormControlModel(property, schema) {
371
- var _a, _b;
372
- if (Array.isArray(property.enum) || ObjectUtils.isString(property.optionsPath)) {
373
- return new DynamicSelectModel(this.getFormSelectConfig(property, schema));
782
+ getFormControlForProperty(property, schema) {
783
+ if (Array.isArray(property.enum)) {
784
+ return createFormSelect(property.id, this.getFormSelectData(property, schema));
374
785
  }
375
786
  switch (property.type) {
376
787
  case "string":
377
788
  case "number":
378
- case "integer":
379
- return new DynamicInputModel(this.getFormInputConfig(property, schema));
380
- case "textarea":
381
- return new DynamicTextAreaModel(this.getFormTextareaConfig(property, schema));
382
789
  case "boolean":
383
- return new DynamicCheckboxModel(this.getFormCheckboxConfig(property, schema));
790
+ case "textarea":
791
+ return createFormInput(property.id, this.getFormInputData(property, schema));
384
792
  case "list":
385
- return new DynamicSelectModel(this.getFormSelectConfig(property, schema));
386
- case "array":
387
- if (((_a = property.items) === null || _a === void 0 ? void 0 : _a.$ref) || property.$ref) {
388
- return new DynamicFormArrayModel(this.getFormArrayConfig(property, schema));
389
- }
390
- else if (((_b = property.items) === null || _b === void 0 ? void 0 : _b.enum) || property.enum) {
391
- return new DynamicSelectModel(this.getFormSelectConfig(property, schema));
392
- }
393
- else {
394
- return new DynamicInputModel(this.getFormInputConfig(property, schema));
395
- }
793
+ return createFormSelect(property.id, this.getFormSelectData(property, schema));
396
794
  case "file":
397
- return new DynamicFileUploadModel(this.getFormUploadConfig(property, schema));
398
- }
399
- if (property.$ref) {
400
- return new DynamicFormGroupModel(this.getFormGroupConfig(property, schema));
795
+ return createFormFile(property.id, this.getFormFileData(property, schema));
401
796
  }
402
797
  return null;
403
798
  }
404
- getFormControlConfig(property, schema) {
405
- const validators = this.getValidators(property, schema);
406
- const errorMessages = Object.keys(validators).reduce((res, key) => {
407
- res[key] = `error.${key}`;
408
- return res;
409
- }, {});
799
+ getBaseFormData(property, schema) {
410
800
  return {
411
- id: property.id,
412
- label: ObjectUtils.isString(property.label) ? property.label : property.id,
413
- hidden: property.hidden,
414
- disabled: property.disabled,
415
- validators,
416
- errorMessages,
417
- additional: Object.assign({}, property)
801
+ readonly: property.readonly ? FormUtilities.readonly : null,
802
+ shouldSerialize: FormUtilities.checkReadonly,
803
+ validators: this.getValidators(property, schema)
418
804
  };
419
805
  }
420
- getFormArrayConfig(property, schema) {
421
- var _a;
422
- const ref = ((_a = property.items) === null || _a === void 0 ? void 0 : _a.$ref) || property.$ref || "";
423
- const subSchema = this.schemas[ref.split("/").pop()];
424
- return Object.assign(this.getFormControlConfig(property, schema), { groupFactory: () => this.getFormModelForSchemaDef(subSchema) });
425
- }
426
- getFormGroupConfig(property, schema) {
427
- const ref = property.$ref || "";
428
- const subSchema = this.schemas[ref.split("/").pop()];
429
- return Object.assign(this.getFormControlConfig(property, schema), { group: this.getFormModelForSchemaDef(subSchema) });
430
- }
431
- getFormInputConfig(property, schema) {
432
- var _a;
433
- let inputType = StringUtils.has(property.id, "password", "Password") ? "password" : (((_a = property.items) === null || _a === void 0 ? void 0 : _a.type) || property.type);
434
- switch (inputType) {
806
+ getFormInputData(property, schema) {
807
+ let type = StringUtils.has(property.id, "password", "Password") ? "password" : property.type;
808
+ switch (property.type) {
435
809
  case "boolean":
436
- inputType = "checkbox";
810
+ type = "checkbox";
437
811
  break;
438
812
  case "textarea":
439
- inputType = "textarea";
440
- break;
441
- case "integer":
442
- inputType = "number";
813
+ type = "textarea";
443
814
  break;
444
815
  }
445
- return Object.assign(this.getFormControlConfig(property, schema), {
446
- inputType,
447
- autoComplete: property.autoComplete || "off",
448
- multiple: property.type == "array",
449
- accept: ObjectUtils.isString(property.accept) ? property.accept : null,
450
- mask: ObjectUtils.isString(property.mask) ? property.mask : null,
451
- pattern: ObjectUtils.isString(property.pattern) ? property.pattern : null,
452
- step: isNaN(property.step) ? 1 : property.step,
453
- min: isNaN(property.min) ? Number.MIN_SAFE_INTEGER : property.min,
454
- max: isNaN(property.max) ? Number.MAX_SAFE_INTEGER : property.max,
455
- });
456
- }
457
- getFormTextareaConfig(property, schema) {
458
- return Object.assign(this.getFormControlConfig(property, schema), {
459
- cols: property.cols || null,
460
- rows: property.rows || 10,
461
- wrap: property.wrap || false,
462
- autoComplete: property.autoComplete || "off",
463
- multiple: property.type == "array"
464
- });
816
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { type });
465
817
  }
466
- getFormSelectConfig(property, schema) {
467
- return Object.assign(this.getFormControlConfig(property, schema), {
468
- options: this.getFormSelectOptions(property, schema),
469
- multiple: property.type == "array"
470
- });
471
- }
472
- getFormCheckboxConfig(property, schema) {
473
- return Object.assign(this.getFormControlConfig(property, schema), {
474
- indeterminate: property.indeterminate === true
475
- });
476
- }
477
- translateOptions(options) {
478
- return __awaiter(this, void 0, void 0, function* () {
479
- if (!options)
480
- return [];
481
- for (const option of options) {
482
- option.label = yield this.language.getTranslation(option.label);
818
+ getFormSelectData(property, schema) {
819
+ const options = Array.isArray(property.enum)
820
+ ? () => {
821
+ return Promise.resolve(property.enum.map(id => ({ id, label: `${property.id}.${id}` })));
483
822
  }
484
- return options;
485
- });
486
- }
487
- getFormSelectOptions(property, schema) {
488
- var _a;
489
- const $enum = ((_a = property.items) === null || _a === void 0 ? void 0 : _a.enum) || property.enum;
490
- if (property.optionsPath) {
491
- return new FormSubject((formModel, control) => {
492
- let path = property.optionsPath;
493
- let target = control;
494
- if (path.startsWith("$root")) {
495
- path = path.substr(5);
496
- while (target.parent) {
497
- target = target.parent;
498
- }
499
- }
500
- while (path.startsWith(".")) {
501
- path = path.substr(1);
502
- if (target.parent) {
503
- target = target.parent;
504
- }
505
- }
506
- const value = ObjectUtils.getValue(target.value, path);
507
- const options = (!ObjectUtils.isArray(value) ? [] : value).map(value => ({ value, label: value }));
508
- return this.translateOptions(options);
509
- });
510
- }
511
- if (ObjectUtils.isArray($enum)) {
512
- return new FormSubject(() => {
513
- const options = $enum.map(value => {
514
- const label = property.translatable ? `${property.id}.${value}` : value;
515
- return { value, label };
516
- });
517
- return this.translateOptions(options);
518
- });
519
- }
520
- return new FormSubject(() => __awaiter(this, void 0, void 0, function* () {
521
- this.api.cache[property.endpoint] = this.api.cache[property.endpoint] || this.api.list(property.endpoint, this.api.makeListParams(1, -1)).then(result => {
522
- return result.items.map(i => {
523
- return { value: i.id || i._id, label: i[property.labelField] || i.label || i.id || i._id };
823
+ : () => {
824
+ this.api.cache[property.endpoint] = this.api.cache[property.endpoint] || this.api.list(property.endpoint, this.api.makeListParams(1, -1)).then(result => {
825
+ return result.items.map(i => {
826
+ return { id: i._id, label: i.name };
827
+ });
524
828
  });
525
- });
526
- const options = (yield this.api.cache[property.endpoint]).map(t => Object.assign({}, t));
527
- return this.translateOptions(options);
528
- }));
829
+ return this.api.cache[property.endpoint];
830
+ };
831
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { options });
529
832
  }
530
- getFormUploadConfig(property, schema) {
531
- const url = this.api.url(property.url || "assets");
532
- const { accept, autoUpload, maxSize, minSize, removeUrl, showFileList } = property;
533
- return Object.assign(this.getFormControlConfig(property, schema), {
534
- url,
535
- accept,
536
- autoUpload,
537
- maxSize,
538
- minSize,
539
- removeUrl,
540
- showFileList
541
- });
833
+ getFormFileData(property, schema) {
834
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { multi: property.multi });
542
835
  }
543
836
  getValidators(property, schema) {
544
- const validators = {};
545
- if (ObjectUtils.isArray(schema.required) && schema.required.indexOf(property.id) >= 0) {
546
- validators.required = null;
837
+ const validators = [];
838
+ if (schema.required.indexOf(property.id) >= 0) {
839
+ validators.push(FormUtilities.validateRequired);
547
840
  }
548
841
  if (property.minLength) {
549
- validators.minLength = property.minLength;
842
+ validators.push({
843
+ type: FormUtilities,
844
+ func: FormUtilities.validateMinLength,
845
+ params: [property.minLength]
846
+ });
550
847
  }
551
848
  if (property.maxLength) {
552
- validators.maxLength = property.maxLength;
553
- }
554
- if (property.min) {
555
- validators.min = property.min;
556
- }
557
- if (property.max) {
558
- validators.max = property.max;
849
+ validators.push({
850
+ type: FormUtilities,
851
+ func: FormUtilities.validateMaxLength,
852
+ params: [property.maxLength]
853
+ });
559
854
  }
560
855
  switch (property.format) {
561
856
  case "email":
562
- validators.email = null;
857
+ validators.push(FormUtilities.validateEmail);
563
858
  break;
564
859
  }
565
860
  return validators;
566
861
  }
567
862
  }
568
- DynamicFormService.decorators = [
863
+ OpenApiService.decorators = [
569
864
  { type: Injectable }
570
865
  ];
571
- DynamicFormService.ctorParameters = () => [
572
- { type: DynamicFormComponentService },
573
- { type: DynamicFormValidationService$1 },
574
- { type: OpenApiService }
866
+ OpenApiService.ctorParameters = () => [
867
+ { type: ApiService }
575
868
  ];
576
869
 
577
870
  class AsyncSubmitDirective {
578
871
  constructor(toaster, cdr, elem, renderer) {
579
872
  this.toaster = toaster;
580
873
  this.cdr = cdr;
581
- this.elem = elem;
582
- this.renderer = renderer;
583
874
  this.onSuccess = new EventEmitter();
584
875
  this.onError = new EventEmitter();
585
876
  if (elem.nativeElement.tagName !== "BUTTON")
@@ -589,26 +880,18 @@ class AsyncSubmitDirective {
589
880
  get isDisabled() {
590
881
  return this.disabled;
591
882
  }
592
- set isDisabled(value) {
593
- this.disabled = value;
594
- if (value) {
595
- this.renderer.setAttribute(this.elem.nativeElement, "disabled", "disabled");
596
- return;
597
- }
598
- this.renderer.removeAttribute(this.elem.nativeElement, "disabled");
599
- }
600
883
  get isLoading() {
601
884
  return this.loading;
602
885
  }
603
886
  ngOnInit() {
604
887
  if (!this.form)
605
888
  return;
606
- this.isDisabled = this.form.status !== "VALID";
889
+ this.disabled = this.form.status !== "VALID";
607
890
  this.cdr.detectChanges();
608
- this.onStatusChange = this.form.onStatusChange.subscribe(() => {
609
- this.isDisabled = this.form.status !== "VALID";
891
+ this.onStatusChange = this.form.onStatusChange.subscribe(form => {
892
+ this.disabled = form.status !== "VALID";
610
893
  this.cdr.detectChanges();
611
- if (!this.callback || this.form.status == "PENDING")
894
+ if (!this.callback || form.status == "PENDING")
612
895
  return;
613
896
  if (!this.disabled) {
614
897
  this.callback();
@@ -634,7 +917,7 @@ class AsyncSubmitDirective {
634
917
  if (this.loading)
635
918
  return;
636
919
  this.loading = true;
637
- this.method(this.form, this.context).then(result => {
920
+ this.method(this.form).then(result => {
638
921
  this.loading = false;
639
922
  if (result) {
640
923
  this.onSuccess.emit(result);
@@ -664,7 +947,6 @@ AsyncSubmitDirective.ctorParameters = () => [
664
947
  AsyncSubmitDirective.propDecorators = {
665
948
  method: [{ type: Input, args: ["async-submit",] }],
666
949
  form: [{ type: Input }],
667
- context: [{ type: Input }],
668
950
  onSuccess: [{ type: Output }],
669
951
  onError: [{ type: Output }],
670
952
  isDisabled: [{ type: HostBinding, args: ["class.disabled",] }],
@@ -672,78 +954,194 @@ AsyncSubmitDirective.propDecorators = {
672
954
  click: [{ type: HostListener, args: ["click",] }]
673
955
  };
674
956
 
675
- class DynamicBaseFormComponent extends DynamicFormComponent {
676
- constructor(formService, events, changeDetectorRef, componentService) {
677
- super(changeDetectorRef, componentService);
957
+ class DynamicFormControlDirective {
958
+ constructor(vcr, forms) {
959
+ this.vcr = vcr;
960
+ this.forms = forms;
961
+ }
962
+ get component() {
963
+ return this.comp;
964
+ }
965
+ ngOnChanges(changes) {
966
+ if (changes.control) {
967
+ this.comp = this.forms.createComponent(this.vcr, this.control.provider);
968
+ }
969
+ if (!this.comp)
970
+ return;
971
+ this.comp.control = this.control;
972
+ }
973
+ }
974
+ DynamicFormControlDirective.decorators = [
975
+ { type: Directive, args: [{
976
+ selector: "[form-control]",
977
+ },] }
978
+ ];
979
+ DynamicFormControlDirective.ctorParameters = () => [
980
+ { type: ViewContainerRef },
981
+ { type: DynamicFormService }
982
+ ];
983
+ DynamicFormControlDirective.propDecorators = {
984
+ control: [{ type: Input, args: ["form-control",] }]
985
+ };
986
+
987
+ class DynamicFormTemplateDirective {
988
+ constructor(template) {
989
+ this.template = template;
990
+ }
991
+ }
992
+ DynamicFormTemplateDirective.decorators = [
993
+ { type: Directive, args: [{
994
+ selector: `ng-template[control],
995
+ ng-template[label],
996
+ ng-template[input],
997
+ ng-template[prefix],
998
+ ng-template[suffix],
999
+ ng-template[setPrefix],
1000
+ ng-template[setSuffix],
1001
+ ng-template[formPrefix],
1002
+ ng-template[formSuffix],
1003
+ ng-template[innerFormPrefix],
1004
+ ng-template[innerFormSuffix]`
1005
+ },] }
1006
+ ];
1007
+ DynamicFormTemplateDirective.ctorParameters = () => [
1008
+ { type: TemplateRef }
1009
+ ];
1010
+ DynamicFormTemplateDirective.propDecorators = {
1011
+ control: [{ type: Input }],
1012
+ label: [{ type: Input }],
1013
+ input: [{ type: Input }],
1014
+ prefix: [{ type: Input }],
1015
+ suffix: [{ type: Input }],
1016
+ setPrefix: [{ type: Input }],
1017
+ setSuffix: [{ type: Input }],
1018
+ formPrefix: [{ type: Input }],
1019
+ formSuffix: [{ type: Input }],
1020
+ innerFormPrefix: [{ type: Input }],
1021
+ innerFormSuffix: [{ type: Input }]
1022
+ };
1023
+
1024
+ class DynamicFormBaseComponent {
1025
+ constructor(cdr, formService) {
678
1026
  this.formService = formService;
679
- this.events = events;
680
- this.blur = new EventEmitter();
681
- this.change = new EventEmitter();
682
- this.focus = new EventEmitter();
1027
+ this.name = "";
1028
+ this.controlTemplates = {};
1029
+ this.labelTemplates = {};
1030
+ this.inputTemplates = {};
1031
+ this.prefixTemplates = {};
1032
+ this.suffixTemplates = {};
1033
+ this.onChange = new EventEmitter();
683
1034
  this.onStatusChange = new EventEmitter();
684
- this.onValueChange = new EventEmitter();
1035
+ this.onInit = new EventEmitter();
685
1036
  this.onSubmit = new EventEmitter();
686
- this.templates = new QueryList();
687
- this.subscription = new Subscription();
688
- this.groupSubscription = new Subscription();
689
- this.labelPrefix = "label";
690
- }
691
- get status() {
692
- return !this.group ? null : this.group.status;
1037
+ this.injector = formService.injector;
1038
+ this.cdr = cdr;
693
1039
  }
694
- ngOnChanges(changes) {
695
- this.groupSubscription.unsubscribe();
696
- if (this.group) {
697
- this.groupSubscription = ObservableUtils.multiSubscription(this.group.statusChanges.subscribe(() => {
698
- this.onStatusChange.emit(this);
699
- }), this.group.valueChanges.subscribe(() => {
700
- this.onValueChange.emit(this);
701
- this.formService.notifyChanges(this.model, this.group);
702
- }));
1040
+ get root() {
1041
+ let form = this;
1042
+ while (ObjectUtils.isDefined(form.parent)) {
1043
+ form = form.parent;
703
1044
  }
1045
+ return form;
704
1046
  }
705
- ngAfterViewInit() {
706
- this.subscription = ObservableUtils.multiSubscription(ObservableUtils.subscribe({
707
- subjects: [this.contentTemplates.changes, this.viewTemplates.changes],
708
- cb: () => {
709
- const templates = this.contentTemplates.toArray().concat(this.viewTemplates.toArray());
710
- this.templates.reset(templates);
711
- }
712
- }), this.events.languageChanged.subscribe(() => {
713
- this.formService.detectChanges(this);
714
- }), this.ngForm.ngSubmit.subscribe(() => {
715
- this.onSubmit.emit(this);
716
- }));
1047
+ findProvider(control) {
1048
+ return this.formService.findProvider(control);
1049
+ }
1050
+ // --- Lifecycle hooks
1051
+ ngAfterContentInit() {
1052
+ this.wrapperTemplate = this.wrapperTemplate || this.cWrapperTemplate;
1053
+ this.fieldSetTemplate = this.fieldSetTemplate || this.cFieldSetTemplate;
1054
+ this.controlTemplate = this.controlTemplate || this.cControlTemplate;
1055
+ this.controlTemplates = this.filterTemplates(this.controlTemplates, "control");
1056
+ this.labelTemplates = this.filterTemplates(this.labelTemplates, "label");
1057
+ this.inputTemplates = this.filterTemplates(this.inputTemplates, "input");
1058
+ this.prefixTemplates = this.filterTemplates(this.prefixTemplates, "prefix");
1059
+ this.suffixTemplates = this.filterTemplates(this.suffixTemplates, "suffix");
1060
+ this.setPrefixTemplates = this.filterTemplates(this.setPrefixTemplates, "setPrefix");
1061
+ this.setSuffixTemplates = this.filterTemplates(this.setSuffixTemplates, "setSuffix");
1062
+ }
1063
+ filterTemplates(templates, type) {
1064
+ if (ObjectUtils.isObject(templates) && Object.keys(templates).length > 0)
1065
+ return templates;
1066
+ return this.templates.filter(t => !!t[type]).reduce((result, directive) => {
1067
+ result[directive[type]] = directive.template;
1068
+ return result;
1069
+ }, {});
717
1070
  }
718
- ngOnDestroy() {
719
- super.ngOnDestroy();
720
- this.subscription.unsubscribe();
721
- this.groupSubscription.unsubscribe();
1071
+ }
1072
+ DynamicFormBaseComponent.decorators = [
1073
+ { type: Directive }
1074
+ ];
1075
+ DynamicFormBaseComponent.ctorParameters = () => [
1076
+ { type: ChangeDetectorRef },
1077
+ { type: DynamicFormService }
1078
+ ];
1079
+ DynamicFormBaseComponent.propDecorators = {
1080
+ name: [{ type: Input }],
1081
+ readonly: [{ type: Input }],
1082
+ updateOn: [{ type: Input }],
1083
+ classes: [{ type: Input }],
1084
+ parent: [{ type: Input }],
1085
+ wrapperTemplate: [{ type: Input }],
1086
+ fieldSetTemplate: [{ type: Input }],
1087
+ controlTemplate: [{ type: Input }],
1088
+ controlTemplates: [{ type: Input }],
1089
+ labelTemplates: [{ type: Input }],
1090
+ inputTemplates: [{ type: Input }],
1091
+ prefixTemplates: [{ type: Input }],
1092
+ suffixTemplates: [{ type: Input }],
1093
+ setPrefixTemplates: [{ type: Input }],
1094
+ setSuffixTemplates: [{ type: Input }],
1095
+ onChange: [{ type: Output }],
1096
+ onStatusChange: [{ type: Output }],
1097
+ onInit: [{ type: Output }],
1098
+ onSubmit: [{ type: Output }],
1099
+ prefixTemplate: [{ type: ContentChild, args: ["prefixTemplate",] }],
1100
+ suffixTemplate: [{ type: ContentChild, args: ["suffixTemplate",] }],
1101
+ templates: [{ type: ContentChildren, args: [DynamicFormTemplateDirective,] }],
1102
+ cWrapperTemplate: [{ type: ContentChild, args: ["wrapperTemplate",] }],
1103
+ cFieldSetTemplate: [{ type: ContentChild, args: ["fieldSetTemplate",] }],
1104
+ cControlTemplate: [{ type: ContentChild, args: ["controlTemplate",] }]
1105
+ };
1106
+
1107
+ class DynamicFormComponent extends DynamicFormBaseComponent {
1108
+ constructor(cdr, forms) {
1109
+ super(cdr, forms);
1110
+ this.group = new DynamicFormGroup(this);
1111
+ this.defaultFieldSet = {
1112
+ id: "",
1113
+ title: "",
1114
+ classes: ""
1115
+ };
722
1116
  }
723
- insertFormArrayGroup(index, formArray, formArrayModel) {
724
- this.formService.insertFormArrayGroup(index, formArray, formArrayModel);
725
- this.changeDetectorRef.detectChanges();
1117
+ get status() {
1118
+ return this.group.state;
726
1119
  }
727
- removeFormArrayGroup(index, formArray, formArrayModel) {
728
- this.formService.removeFormArrayGroup(index, formArray, formArrayModel);
729
- this.changeDetectorRef.detectChanges();
1120
+ get formControls() {
1121
+ return this.group.formControls;
730
1122
  }
731
- moveFormArrayGroup(index, step, formArray, formArrayModel) {
732
- this.formService.moveFormArrayGroup(index, step, formArray, formArrayModel);
733
- this.changeDetectorRef.detectChanges();
1123
+ // --- Lifecycle hooks
1124
+ ngOnChanges(changes) {
1125
+ if (!this.data)
1126
+ return;
1127
+ if (changes.data || changes.controls || changes.serializers || changes.formGroup) {
1128
+ if (this.group.id)
1129
+ return;
1130
+ this.group.setup(this.data, this);
1131
+ this.group.reloadControls();
1132
+ }
734
1133
  }
735
- clearFormArray(formArray, formArrayModel) {
736
- this.formService.clearFormArray(formArray, formArrayModel);
737
- this.changeDetectorRef.detectChanges();
1134
+ // --- Custom ---
1135
+ onFormSubmit() {
1136
+ const root = this.root;
1137
+ root.validate().then(() => root.onSubmit.emit(this), () => { });
738
1138
  }
1139
+ // --- IDynamicForm ---
739
1140
  validate(showErrors = true) {
740
- if (!this.group)
741
- return Promise.resolve();
742
1141
  return new Promise((resolve, reject) => {
743
1142
  this.group.statusChanges.pipe(first(status => status == "VALID" || status == "INVALID")).subscribe(status => {
744
- if (showErrors) {
745
- this.formService.showErrors(this);
746
- }
1143
+ if (showErrors)
1144
+ this.group.showErrors();
747
1145
  if (status == "VALID") {
748
1146
  resolve(null);
749
1147
  return;
@@ -754,172 +1152,547 @@ class DynamicBaseFormComponent extends DynamicFormComponent {
754
1152
  });
755
1153
  }
756
1154
  serialize(validate) {
757
- return __awaiter(this, void 0, void 0, function* () {
758
- if (!this.group)
759
- return null;
1155
+ return new Promise((resolve, reject) => {
1156
+ const serialize = () => {
1157
+ this.group.serialize().then(resolve);
1158
+ };
760
1159
  if (validate) {
761
- yield this.validate();
1160
+ this.validate().then(serialize, reject);
1161
+ return;
762
1162
  }
763
- return yield this.formService.serialize(this.model, this.group);
1163
+ serialize();
764
1164
  });
765
1165
  }
1166
+ check() {
1167
+ return this.group.check();
1168
+ }
1169
+ getControl(id) {
1170
+ return this.group.getControl(id);
1171
+ }
766
1172
  }
767
- DynamicBaseFormComponent.decorators = [
1173
+ DynamicFormComponent.decorators = [
768
1174
  { type: Component, args: [{
769
- selector: "dynamic-base-form",
770
- template: "",
771
- changeDetection: ChangeDetectionStrategy.Default
1175
+ moduleId: module.id,
1176
+ selector: "dynamic-form, [dynamic-form]",
1177
+ template: "<ng-template #defaultFieldSetTemplate let-id=\"id\" let-controls=\"controls\" let-fieldSet=\"fieldSet\">\r\n <div [ngClass]=\"['form-fields', 'form-fields-' + id, fieldSet.classes]\">\r\n <ng-container [ngxTemplateOutlet]=\"setPrefixTemplates[fieldSet.id]\" [context]=\"fieldSet\"></ng-container>\r\n <div *ngIf=\"fieldSet.title\" [ngClass]=\"['form-title', fieldSet.titleClasses || '']\">\r\n {{ group.prefix + fieldSet.title | translate }}\r\n </div>\r\n <ng-template #subControls>\r\n <ng-container *ngFor=\"let control of controls\" [form-group]=\"control\" [visible]=\"control.visible\" [form]=\"this\">\r\n\r\n </ng-container>\r\n </ng-template>\r\n <div *ngIf=\"fieldSet.setClasses; else subControls\" [ngClass]=\"fieldSet.setClasses\">\r\n <div *ngIf=\"fieldSet.controlClasses; else subControls\" [ngClass]=\"fieldSet.controlClasses\">\r\n <ng-container [ngTemplateOutlet]=\"subControls\"></ng-container>\r\n </div>\r\n </div>\r\n <ng-container [ngxTemplateOutlet]=\"setSuffixTemplates[fieldSet.id]\" [context]=\"fieldSet\"></ng-container>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #fieldSetsTemplate>\r\n <ng-container *ngFor=\"let fs of formControls | groupBy:'data.fieldSet'\"\r\n [ngxTemplateOutlet]=\"fieldSetTemplate || defaultFieldSetTemplate\"\r\n [context]=\"{form: this, id: fs.group, controls: fs.items, fieldSet: group.formFields[fs.group] || defaultFieldSet}\">\r\n </ng-container>\r\n</ng-template>\r\n<ng-template #defaultWrapperTemplate let-form=\"form\" let-fieldSetsTemplate=\"fieldSetsTemplate\">\r\n <ng-container [ngTemplateOutlet]=\"prefixTemplate\"\r\n [ngTemplateOutletContext]=\"{form: form, fieldSetsTemplate: fieldSetsTemplate}\"></ng-container>\r\n <form class=\"dynamic-form\" [ngClass]=\"form.classes || ''\" [formGroup]=\"group\" (submit)=\"onFormSubmit()\">\r\n <ng-container [ngTemplateOutlet]=\"fieldSetsTemplate\"></ng-container>\r\n <ng-content></ng-content>\r\n <div *ngIf=\"form.status == 'PENDING'\" class=\"dynamic-form-validator\">\r\n {{ group.prefix + 'message.pending' | translate }}\r\n </div>\r\n <button [ngStyle]=\"{display: 'none'}\">Submit</button>\r\n </form>\r\n <ng-container [ngTemplateOutlet]=\"suffixTemplate\"\r\n [ngTemplateOutletContext]=\"{form: form, fieldSetsTemplate: fieldSetsTemplate}\"></ng-container>\r\n</ng-template>\r\n<ng-template #loadingTemplate>\r\n <div *ngIf=\"status == 'LOADING'\" class=\"dynamic-form-loader\">\r\n {{ group.prefix + 'message.loading' | translate }}\r\n </div>\r\n</ng-template>\r\n<ng-container [ngTemplateOutlet]=\"wrapperTemplate || defaultWrapperTemplate\"\r\n [ngTemplateOutletContext]=\"{form: this, fieldSetsTemplate: fieldSetsTemplate}\"\r\n *ngIf=\"status !== 'LOADING'; else loadingTemplate\">\r\n</ng-container>\r\n",
1178
+ providers: [{ provide: DynamicFormBaseComponent, useExisting: DynamicFormComponent }]
772
1179
  },] }
773
1180
  ];
774
- DynamicBaseFormComponent.ctorParameters = () => [
775
- { type: DynamicFormService, decorators: [{ type: Inject, args: [DynamicFormService,] }] },
776
- { type: EventsService, decorators: [{ type: Inject, args: [EventsService,] }] },
1181
+ DynamicFormComponent.ctorParameters = () => [
777
1182
  { type: ChangeDetectorRef },
778
- { type: DynamicFormComponentService }
1183
+ { type: DynamicFormService }
779
1184
  ];
780
- DynamicBaseFormComponent.propDecorators = {
1185
+ DynamicFormComponent.propDecorators = {
781
1186
  group: [{ type: Input }],
782
- model: [{ type: Input }],
783
- layout: [{ type: Input }],
784
- labelPrefix: [{ type: Input }],
785
- blur: [{ type: Output }],
786
- change: [{ type: Output }],
787
- focus: [{ type: Output }],
788
- contentTemplates: [{ type: ContentChildren, args: [DynamicTemplateDirective,] }],
789
- viewTemplates: [{ type: ViewChildren, args: [DynamicTemplateDirective,] }],
790
- onStatusChange: [{ type: Output }],
791
- onValueChange: [{ type: Output }],
792
- onSubmit: [{ type: Output }],
793
- ngForm: [{ type: ViewChild, args: [NgForm,] }]
1187
+ controls: [{ type: Input }],
1188
+ serializers: [{ type: Input }],
1189
+ fieldSets: [{ type: Input }],
1190
+ data: [{ type: Input }]
794
1191
  };
795
1192
 
796
- class DynamicBaseFormControlContainerComponent extends DynamicFormControlContainerComponent {
797
- constructor(form, changeDetectorRef, componentFactoryResolver, layoutService, validationService, componentService, relationService) {
798
- super(changeDetectorRef, componentFactoryResolver, layoutService, validationService, componentService, relationService);
799
- this.form = form;
800
- this.changeDetectorRef = changeDetectorRef;
801
- this.componentFactoryResolver = componentFactoryResolver;
802
- this.layoutService = layoutService;
803
- this.validationService = validationService;
804
- this.componentService = componentService;
805
- this.relationService = relationService;
806
- this.context = null;
807
- this.blur = new EventEmitter();
808
- this.change = new EventEmitter();
809
- this.focus = new EventEmitter();
810
- }
811
- get componentType() {
812
- var _a;
813
- return (_a = this.componentService.getCustomComponentType(this.model)) !== null && _a !== void 0 ? _a : this.getComponentType(this.model);
814
- }
815
- get startTemplate() {
816
- return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
817
- ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_START")
818
- : this.layoutService.getStartTemplate(this.model, this.templates);
819
- }
820
- get endTemplate() {
821
- return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
822
- ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_END")
823
- : this.layoutService.getEndTemplate(this.model, this.templates);
824
- }
825
- get formService() {
826
- return this.form.formService;
1193
+ const statusPriority = ["LOADING", "PENDING", "DISABLED", "INVALID"];
1194
+ class DynamicFormsComponent extends DynamicFormBaseComponent {
1195
+ constructor(cdr, formService) {
1196
+ super(cdr, formService);
1197
+ this.formPrefixTemplates = {};
1198
+ this.formSuffixTemplates = {};
1199
+ this.innerFormPrefixTemplates = {};
1200
+ this.innerFormSuffixTemplates = {};
827
1201
  }
828
- ngOnInit() {
829
- this.onDetectChanges = this.formService.onDetectChanges.subscribe(form => {
830
- if (form !== this.form)
1202
+ get status() {
1203
+ for (let i = 0; i < statusPriority.length; i++) {
1204
+ const status = statusPriority[i];
1205
+ if (this.checkForms(f => f.status == status))
1206
+ return status;
1207
+ }
1208
+ return "VALID";
1209
+ }
1210
+ // --- Lifecycle hooks
1211
+ ngAfterContentInit() {
1212
+ super.ngAfterContentInit();
1213
+ this.containerTemplate = this.containerTemplate || this.cContainerTemplate;
1214
+ this.formPrefixTemplates = this.filterTemplates(this.formPrefixTemplates, "formPrefix");
1215
+ this.formSuffixTemplates = this.filterTemplates(this.formSuffixTemplates, "formSuffix");
1216
+ this.innerFormPrefixTemplates = this.filterTemplates(this.innerFormPrefixTemplates, "innerFormPrefix");
1217
+ this.innerFormSuffixTemplates = this.filterTemplates(this.innerFormSuffixTemplates, "innerFormSuffix");
1218
+ }
1219
+ ngOnChanges(changes) {
1220
+ this.configs = this.createFormGroups(this.data);
1221
+ }
1222
+ // --- IDynamicFormBase ---
1223
+ validate(showErrors = true) {
1224
+ if (!this.forms)
1225
+ return Promise.reject(null);
1226
+ return Promise.all(this.forms.map(f => f.validate(showErrors)));
1227
+ }
1228
+ serialize(validate) {
1229
+ if (!this.forms)
1230
+ return validate ? Promise.reject(null) : Promise.resolve({});
1231
+ return new Promise((resolve, reject) => {
1232
+ const promises = this.forms.map(f => f.serialize(validate));
1233
+ Promise.all(promises).then(results => {
1234
+ let result = {};
1235
+ results.forEach((data, ix) => {
1236
+ const config = this.data[ix];
1237
+ let path = null;
1238
+ if (ObjectUtils.isArray(config.path) && config.path.length > 0) {
1239
+ path = config.path;
1240
+ }
1241
+ else if (ObjectUtils.isString(config.path) && config.path.length > 0) {
1242
+ path = config.path.split(".");
1243
+ }
1244
+ else if (ObjectUtils.isNumber(config.path)) {
1245
+ path = [config.path];
1246
+ }
1247
+ if (!path) {
1248
+ result = ObjectUtils.assign(result, data);
1249
+ return;
1250
+ }
1251
+ result = ObjectUtils.mapToPath(result, data, path.map(p => `${p}`));
1252
+ });
1253
+ resolve(result);
1254
+ }, reject);
1255
+ });
1256
+ }
1257
+ check() {
1258
+ if (!this.forms)
1259
+ return Promise.resolve(null);
1260
+ return Promise.all(this.forms.map(t => t.check()));
1261
+ }
1262
+ getControl(id) {
1263
+ return this.getFromValue(f => f.getControl(id));
1264
+ }
1265
+ checkForms(check) {
1266
+ this.cdr.detectChanges();
1267
+ if (!this.forms)
1268
+ return false;
1269
+ return ObjectUtils.isDefined(this.forms.find(check));
1270
+ }
1271
+ getFromValue(check) {
1272
+ if (!this.forms)
1273
+ return null;
1274
+ let value = null;
1275
+ this.forms.find(f => {
1276
+ value = check(f);
1277
+ return ObjectUtils.isDefined(value);
1278
+ });
1279
+ return value;
1280
+ }
1281
+ createFormGroups(configs) {
1282
+ return (configs || []).map((c) => {
1283
+ if (c.multi)
1284
+ return c;
1285
+ const config = c;
1286
+ const group = new DynamicFormGroup(this, {
1287
+ id: config.id || UniqueUtils.uuid(),
1288
+ type: "model",
1289
+ data: config.controlData
1290
+ });
1291
+ config.group = group;
1292
+ config.name = config.name || this.name;
1293
+ group.setup(config.data, config);
1294
+ group.reloadControls();
1295
+ return config;
1296
+ });
1297
+ }
1298
+ }
1299
+ DynamicFormsComponent.decorators = [
1300
+ { type: Component, args: [{
1301
+ moduleId: module.id,
1302
+ selector: "dynamic-forms, [dynamic-forms]",
1303
+ template: "<ng-template #configTemplate let-formTemplate=\"formTemplate\" let-form=\"form\" let-config=\"config\" let-configTemplate>\r\n <div [ngClass]=\"config.classes || ''\">\r\n <ng-container [ngTemplateOutlet]=\"formPrefixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: form}\"></ng-container>\r\n <ng-template #singleFormTemplate>\r\n <div dynamic-form\r\n [hidden]=\"!config.group?.visible\"\r\n [ngClass]=\"config.formClasses || ''\"\r\n [name]=\"config.name || form.name\"\r\n [readonly]=\"form.readonly\"\r\n [updateOn]=\"form.updateOn\"\r\n [classes]=\"config.innerFormClasses\"\r\n [parent]=\"form\"\r\n\r\n [wrapperTemplate]=\"form.wrapperTemplate\"\r\n [fieldSetTemplate]=\"form.fieldSetTemplate\"\r\n [controlTemplates]=\"form.controlTemplates\"\r\n\r\n [controlTemplate]=\"form.controlTemplate\"\r\n [labelTemplates]=\"form.labelTemplates\"\r\n [inputTemplates]=\"form.inputTemplates\"\r\n [prefixTemplates]=\"form.prefixTemplates\"\r\n [suffixTemplates]=\"form.suffixTemplates\"\r\n [setPrefixTemplates]=\"form.setPrefixTemplates\"\r\n [setSuffixTemplates]=\"form.setSuffixTemplates\"\r\n\r\n [group]=\"config.group\"\r\n [fieldSets]=\"config.fieldSets\">\r\n <ng-template #prefixTemplate let-subForm=\"form\">\r\n <ng-container [ngTemplateOutlet]=\"form.innerFormPrefixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: subForm}\"></ng-container>\r\n </ng-template>\r\n <ng-template #suffixTemplate let-subForm=\"form\">\r\n <ng-container [ngTemplateOutlet]=\"form.innerFormSuffixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: subForm}\"></ng-container>\r\n </ng-template>\r\n </div>\r\n </ng-template>\r\n <div dynamic-forms\r\n [ngClass]=\"config.formClasses || ''\"\r\n [name]=\"config.name || name\"\r\n [readonly]=\"form.readonly\"\r\n [updateOn]=\"form.updateOn\"\r\n [classes]=\"config.innerFormClasses\"\r\n [parent]=\"form\"\r\n\r\n [wrapperTemplate]=\"form.wrapperTemplate\"\r\n [fieldSetTemplate]=\"form.fieldSetTemplate\"\r\n [controlTemplates]=\"form.controlTemplates\"\r\n\r\n [controlTemplate]=\"form.controlTemplate\"\r\n [labelTemplates]=\"form.labelTemplates\"\r\n [inputTemplates]=\"form.inputTemplates\"\r\n [prefixTemplates]=\"form.prefixTemplates\"\r\n [suffixTemplates]=\"form.suffixTemplates\"\r\n [setPrefixTemplates]=\"form.setPrefixTemplates\"\r\n [setSuffixTemplates]=\"form.setSuffixTemplates\"\r\n\r\n [data]=\"config.data\"\r\n [containerTemplate]=\"form.containerTemplate\"\r\n [formPrefixTemplates]=\"form.formPrefixTemplates\"\r\n [formSuffixTemplates]=\"form.formSuffixTemplates\"\r\n [innerFormPrefixTemplates]=\"form.innerFormPrefixTemplates\"\r\n [innerFormSuffixTemplates]=\"form.innerFormSuffixTemplates\"\r\n\r\n *ngIf=\"config.multi; else singleFormTemplate\">\r\n <ng-template #prefixTemplate let-subForm=\"form\">\r\n <ng-container [ngTemplateOutlet]=\"form.innerFormPrefixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: subForm}\"></ng-container>\r\n </ng-template>\r\n <ng-template #suffixTemplate let-subForm=\"form\">\r\n <ng-container [ngTemplateOutlet]=\"form.innerFormSuffixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: subForm}\"></ng-container>\r\n </ng-template>\r\n </div>\r\n <ng-container [ngTemplateOutlet]=\"formSuffixTemplates[config.id]\"\r\n [ngTemplateOutletContext]=\"{config: config, form: form}\"></ng-container>\r\n </div>\r\n</ng-template>\r\n<ng-template let-form=\"form\" let-configTemplate=\"configTemplate\" #defaultContainerTemplate>\r\n <div class=\"dynamic-forms\" [ngClass]=\"classes || ''\">\r\n <ng-container *ngFor=\"let config of form.configs\"\r\n [ngTemplateOutlet]=\"configTemplate\"\r\n [ngTemplateOutletContext]=\"{form: form, config: config}\"></ng-container>\r\n <ng-content></ng-content>\r\n </div>\r\n</ng-template>\r\n<ng-container [ngTemplateOutlet]=\"prefixTemplate\"\r\n [ngTemplateOutletContext]=\"{form: this}\"></ng-container>\r\n<ng-container [ngTemplateOutlet]=\"containerTemplate || defaultContainerTemplate\"\r\n [ngTemplateOutletContext]=\"{form: this, configTemplate: configTemplate, defaultContainerTemplate: defaultContainerTemplate}\">\r\n</ng-container>\r\n<ng-container [ngTemplateOutlet]=\"suffixTemplate\"\r\n [ngTemplateOutletContext]=\"{form: this}\"></ng-container>\r\n",
1304
+ providers: [{ provide: DynamicFormBaseComponent, useExisting: DynamicFormsComponent }]
1305
+ },] }
1306
+ ];
1307
+ DynamicFormsComponent.ctorParameters = () => [
1308
+ { type: ChangeDetectorRef },
1309
+ { type: DynamicFormService }
1310
+ ];
1311
+ DynamicFormsComponent.propDecorators = {
1312
+ data: [{ type: Input }],
1313
+ containerTemplate: [{ type: Input }],
1314
+ formPrefixTemplates: [{ type: Input }],
1315
+ formSuffixTemplates: [{ type: Input }],
1316
+ innerFormPrefixTemplates: [{ type: Input }],
1317
+ innerFormSuffixTemplates: [{ type: Input }],
1318
+ cContainerTemplate: [{ type: ContentChild, args: ["containerTemplate",] }],
1319
+ forms: [{ type: ViewChildren, args: [DynamicFormBaseComponent,] }]
1320
+ };
1321
+
1322
+ class DynamicFormGroupComponent {
1323
+ get classes() {
1324
+ if (!this.control)
1325
+ return "form-group";
1326
+ return ["form-group", "form-group-" + this.control.id, this.control.data.classes, this.control.errors && this.control.touched ? "form-group-invalid" : ""].join(" ");
1327
+ }
1328
+ }
1329
+ DynamicFormGroupComponent.decorators = [
1330
+ { type: Component, args: [{
1331
+ moduleId: module.id,
1332
+ selector: "div[dynamic-form-group]",
1333
+ template: "<ng-template #labelTemplate>\r\n <label [ngClass]=\"'control-label control-label-' + control.data.labelAlign\" [attr.for]=\"control.formId\">\r\n {{ control.label | translate: control.model }}\r\n </label>\r\n</ng-template>\r\n<ng-template #errorTemplate>\r\n <div class=\"error-message\" *ngIf=\"control.errors && control.touched\">\r\n <span class=\"help-block\" *ngFor=\"let error of control.errors | entries\">\r\n {{ error.key | translate: error.value }}\r\n </span>\r\n </div>\r\n</ng-template>\r\n<ng-template #inputTemplate let-control=\"control\">\r\n <ng-container [form-control]=\"control\"></ng-container>\r\n</ng-template>\r\n<ng-template #defaultControlTemplate\r\n let-context\r\n let-control=\"control\"\r\n let-data=\"control.data\"\r\n let-labelTemplate=\"labelTemplate\"\r\n let-inputTemplate=\"inputTemplate\"\r\n let-prefixTemplate=\"prefixTemplate\"\r\n let-suffixTemplate=\"suffixTemplate\"\r\n let-errorTemplate=\"errorTemplate\">\r\n <ng-container [ngTemplateOutlet]=\"prefixTemplate\"\r\n [ngTemplateOutletContext]=\"context\"></ng-container>\r\n <ng-container *ngIf=\"control.label && data.labelAlign == 'left'\"\r\n [ngTemplateOutlet]=\"labelTemplate\"\r\n [ngTemplateOutletContext]=\"context\">\r\n </ng-container>\r\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"\r\n [ngTemplateOutletContext]=\"context\"></ng-container>\r\n <ng-container *ngIf=\"control.label && data.labelAlign == 'right'\"\r\n [ngTemplateOutlet]=\"labelTemplate\"\r\n [ngTemplateOutletContext]=\"context\">\r\n </ng-container>\r\n <ng-container [ngTemplateOutlet]=\"suffixTemplate\"\r\n [ngTemplateOutletContext]=\"context\"></ng-container>\r\n <ng-container [ngTemplateOutlet]=\"errorTemplate\"\r\n [ngTemplateOutletContext]=\"context\"></ng-container>\r\n</ng-template>\r\n\r\n<ng-container [ngxTemplateOutlet]=\"form.controlTemplates[control.id] || form.controlTemplate || defaultControlTemplate\"\r\n [context]=\"{\r\n control: control,\r\n labelTemplate: form.labelTemplates[control.id] || labelTemplate,\r\n inputTemplate: form.inputTemplates[control.id] || inputTemplate,\r\n prefixTemplate: form.prefixTemplates[control.id],\r\n suffixTemplate: form.suffixTemplates[control.id],\r\n errorTemplate: errorTemplate\r\n }\">\r\n</ng-container>\r\n"
1334
+ },] }
1335
+ ];
1336
+ DynamicFormGroupComponent.propDecorators = {
1337
+ classes: [{ type: HostBinding, args: ["class",] }]
1338
+ };
1339
+
1340
+ class DynamicFormFileComponent extends FormControlComponent {
1341
+ constructor(api, toaster) {
1342
+ super();
1343
+ this.api = api;
1344
+ this.toaster = toaster;
1345
+ this.fileImageCache = [];
1346
+ }
1347
+ // Acceptor for provider
1348
+ static acceptor(control) {
1349
+ return control.type == "file";
1350
+ }
1351
+ // Loader for provider
1352
+ static loader() {
1353
+ return Promise.resolve();
1354
+ }
1355
+ onSelect(input) {
1356
+ this.processFiles(input.files);
1357
+ input.value = "";
1358
+ }
1359
+ processFiles(fileList) {
1360
+ const files = [];
1361
+ const accept = this.data.accept;
1362
+ const types = ObjectUtils.isString(accept) && accept.length > 0 ? accept.toLowerCase().split(",") : null;
1363
+ if (fileList.length == 0)
1364
+ return;
1365
+ for (let i = 0; i < fileList.length; i++) {
1366
+ const file = fileList.item(i);
1367
+ const type = file.type.toLowerCase();
1368
+ const ext = FileUtils.getExtension(file);
1369
+ if (types && !ArrayUtils.has(types, type, ext))
1370
+ continue;
1371
+ files.push(file);
1372
+ }
1373
+ if (files.length == 0) {
1374
+ this.toaster.error("message.error.wrong-files");
1375
+ return;
1376
+ }
1377
+ this.upload(files);
1378
+ }
1379
+ upload(files) {
1380
+ const single = !this.data.multi;
1381
+ if (single)
1382
+ files.length = Math.min(files.length, 1);
1383
+ const promises = [];
1384
+ files.forEach((file, ix) => {
1385
+ if (this.data.asDataUrl) {
1386
+ promises.push(FileUtils.readFileAsDataURL(file));
1387
+ return;
1388
+ }
1389
+ if (this.data.asFile) {
1390
+ promises.push(Promise.resolve(file));
831
1391
  return;
832
- this.changeDetectorRef.detectChanges();
833
- this.formService.updateSelectOptions(this.model, this.control);
1392
+ }
1393
+ promises.push(this.api.upload(this.data.uploadUrl, this.data.createUploadData(file), console.log, this.data.uploadOptions).then(asset => asset._id || asset, () => null));
1394
+ });
1395
+ Promise.all(promises).then(assets => {
1396
+ if (single) {
1397
+ this.control.setValue(assets[0]);
1398
+ return;
1399
+ }
1400
+ const current = this.value || [];
1401
+ this.control.setValue(current.concat(assets.filter(t => !!t)));
834
1402
  });
835
1403
  }
836
- ngOnDestroy() {
837
- super.ngOnDestroy();
838
- this.onDetectChanges.unsubscribe();
839
- }
840
- createFormControlComponent() {
841
- var _a;
842
- super.createFormControlComponent();
843
- const component = (_a = this.componentRef) === null || _a === void 0 ? void 0 : _a.instance;
844
- if (!component || !ObjectUtils.isFunction(component.onCreated))
1404
+ delete(index) {
1405
+ if (this.data.multi) {
1406
+ const current = Array.from(this.value || []);
1407
+ current.splice(index, 1);
1408
+ this.control.setValue(current);
845
1409
  return;
846
- component.onCreated();
1410
+ }
1411
+ this.control.setValue(null);
847
1412
  }
848
- getComponentType(model) {
849
- return null;
1413
+ getUrl(image) {
1414
+ return `url('${this.getImgUrl(image)}')`;
1415
+ }
1416
+ getImgUrl(image) {
1417
+ if (ObjectUtils.isBlob(image)) {
1418
+ let cache = this.fileImageCache.find(t => t.file == image);
1419
+ if (!cache) {
1420
+ cache = { file: image, url: URL.createObjectURL(image) };
1421
+ this.fileImageCache.push(cache);
1422
+ }
1423
+ return cache.url;
1424
+ }
1425
+ const url = !image ? null : image.imageUrl || image;
1426
+ if (!ObjectUtils.isString(url))
1427
+ return null;
1428
+ if (url.startsWith("data:"))
1429
+ return url;
1430
+ if (!this.data.baseUrl) {
1431
+ const subUrl = url.startsWith("/") ? url.substr(1) : url;
1432
+ return this.api.url(subUrl);
1433
+ }
1434
+ return this.api.url(`${this.data.baseUrl}${url}`);
850
1435
  }
851
1436
  }
852
- DynamicBaseFormControlContainerComponent.decorators = [
1437
+ DynamicFormFileComponent.decorators = [
853
1438
  { type: Component, args: [{
854
- selector: "dynamic-base-form-control",
855
- template: "",
856
- changeDetection: ChangeDetectionStrategy.OnPush
1439
+ selector: "dynamic-form-file",
1440
+ template: "<div class=\"upload\">\r\n <input type=\"file\" (change)=\"onSelect($event.target)\" [attr.accept]=\"data.accept\" multiple=\"multiple\"/>\r\n <ul class=\"images\" *ngIf=\"!data.multi\">\r\n <li class=\"image\" *ngIf=\"value\" [ngStyle]=\"{backgroundImage: getUrl(value)}\">\r\n <a class=\"btn btn-delete\" (click)=\"delete()\"></a>\r\n </li>\r\n </ul>\r\n <ul class=\"images\" *ngIf=\"data.multi\">\r\n <li class=\"image\" *ngFor=\"let image of value; let i = index\" [ngStyle]=\"{backgroundImage: getUrl(image)}\">\r\n <a class=\"btn btn-delete\" (click)=\"delete(i)\"></a>\r\n </li>\r\n </ul>\r\n</div>\r\n",
1441
+ styles: [".upload{display:inline-block}.upload ul{display:inline-flex;list-style:none;margin:10px 0 0;padding:0}.upload ul li{height:100px;width:100px;background:repeating-linear-gradient(45deg,gray,gray 10px,lightgray 10px,lightgray 20px) center center;background-size:cover;border:1px gray solid;margin-right:5px;position:relative}.upload .btn-delete{background:linear-gradient(to bottom,#c8607a,#a64d5a);border-color:#a64d5a;color:#fff;position:absolute;padding:0;right:5px;top:5px;width:20px;height:20px}.upload .btn-delete:before{color:#fff;display:block;position:absolute;top:0;right:6px;content:\"x\"}\n"]
857
1442
  },] }
858
1443
  ];
859
- DynamicBaseFormControlContainerComponent.ctorParameters = () => [
860
- { type: DynamicBaseFormComponent },
861
- { type: ChangeDetectorRef },
862
- { type: ComponentFactoryResolver },
863
- { type: DynamicFormLayoutService },
864
- { type: DynamicFormValidationService$1 },
865
- { type: DynamicFormComponentService },
866
- { type: DynamicFormRelationService }
1444
+ DynamicFormFileComponent.ctorParameters = () => [
1445
+ { type: ApiService },
1446
+ { type: undefined, decorators: [{ type: Inject, args: [TOASTER_SERVICE,] }] }
1447
+ ];
1448
+
1449
+ class DynamicFormInputComponent extends FormControlComponent {
1450
+ constructor(language) {
1451
+ super();
1452
+ this.language = language;
1453
+ }
1454
+ // Acceptor for provider
1455
+ static acceptor(control) {
1456
+ return control.type == "input";
1457
+ }
1458
+ // Loader for provider
1459
+ static loader() {
1460
+ return Promise.resolve();
1461
+ }
1462
+ get isChecked() {
1463
+ return this.data.type == "checkbox" && this.value;
1464
+ }
1465
+ onDateChange(value) {
1466
+ const date = new Date(value);
1467
+ const dateValue = date.valueOf();
1468
+ if (isNaN(dateValue) || dateValue < -30610224000000)
1469
+ return;
1470
+ this.control.setValue(date);
1471
+ }
1472
+ onTextChange(value) {
1473
+ if (!this.data.useLanguage) {
1474
+ this.control.setValue(value);
1475
+ return;
1476
+ }
1477
+ const translations = ObjectUtils.isArray(this.value) ? Array.from(this.value) : [];
1478
+ const translation = translations.find(t => t.lang == this.language.editLanguage);
1479
+ if (translation) {
1480
+ translation.translation = value;
1481
+ }
1482
+ else {
1483
+ translations.push({
1484
+ lang: this.language.editLanguage,
1485
+ translation: value
1486
+ });
1487
+ }
1488
+ this.control.setValue(translations);
1489
+ }
1490
+ onNumberBlur() {
1491
+ const value = this.value;
1492
+ if (ObjectUtils.isNumber(this.data.max) && this.data.max < value) {
1493
+ this.control.setValue(this.data.max);
1494
+ }
1495
+ else if (ObjectUtils.isNumber(this.data.min) && this.data.min > value) {
1496
+ this.control.setValue(this.data.min);
1497
+ }
1498
+ this.control.onBlur();
1499
+ }
1500
+ }
1501
+ DynamicFormInputComponent.decorators = [
1502
+ { type: Component, args: [{
1503
+ moduleId: module.id,
1504
+ selector: "dynamic-form-input",
1505
+ template: "<ng-container [ngSwitch]=\"data.type\">\r\n <ng-container *ngSwitchCase=\"'textarea'\">\r\n <textarea class=\"form-control\"\r\n rows=\"5\"\r\n [attr.autocomplete]=\"data.autocomplete\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"control.setValue($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\">{{ data.placeholder | translate }}</textarea>\r\n </ng-container>\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n <input class=\"form-control\"\r\n type=\"date\"\r\n [attr.autocomplete]=\"data.autocomplete\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"value | date: 'y-MM-dd'\"\r\n (ngModelChange)=\"onDateChange($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\"/>\r\n </ng-container>\r\n <ng-container *ngSwitchCase=\"'number'\">\r\n <input class=\"form-control\"\r\n type=\"number\"\r\n [attr.autocomplete]=\"data.autocomplete\"\r\n [min]=\"data.min\"\r\n [max]=\"data.max\"\r\n [step]=\"data.step\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"control.setValue($event)\"\r\n (blur)=\"onNumberBlur()\"\r\n (focus)=\"control.onFocus()\"/>\r\n </ng-container>\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <input class=\"form-control\"\r\n type=\"checkbox\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"control.setValue($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\"/>\r\n </ng-container>\r\n <ng-container *ngSwitchCase=\"'text'\">\r\n <input class=\"form-control\"\r\n type=\"text\"\r\n [attr.autocomplete]=\"data.autocomplete\"\r\n [minlength]=\"data.min\"\r\n [maxlength]=\"data.max\"\r\n [placeholder]=\"data.placeholder | translate\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"data.useLanguage ? (value | translate) : value\"\r\n (ngModelChange)=\"onTextChange($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\"/>\r\n </ng-container>\r\n <ng-container *ngSwitchDefault>\r\n <input class=\"form-control\"\r\n [attr.autocomplete]=\"data.autocomplete\"\r\n [minlength]=\"data.min\"\r\n [maxlength]=\"data.max\"\r\n [placeholder]=\"data.placeholder | translate\"\r\n [type]=\"data.type\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"control.setValue($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\"/>\r\n </ng-container>\r\n</ng-container>\r\n"
1506
+ },] }
867
1507
  ];
868
- DynamicBaseFormControlContainerComponent.propDecorators = {
869
- contentTemplateList: [{ type: ContentChildren, args: [DynamicTemplateDirective,] }],
870
- klass: [{ type: HostBinding, args: ["class",] }],
871
- context: [{ type: Input }],
872
- group: [{ type: Input }],
873
- hostClass: [{ type: Input }],
874
- inputTemplateList: [{ type: Input, args: ["templates",] }],
875
- layout: [{ type: Input }],
876
- model: [{ type: Input }],
877
- blur: [{ type: Output }],
878
- change: [{ type: Output }],
879
- focus: [{ type: Output }],
880
- componentViewContainerRef: [{ type: ViewChild, args: ["componentViewContainer", { read: ViewContainerRef, static: true },] }]
1508
+ DynamicFormInputComponent.ctorParameters = () => [
1509
+ { type: undefined, decorators: [{ type: Inject, args: [LANGUAGE_SERVICE,] }] }
1510
+ ];
1511
+ DynamicFormInputComponent.propDecorators = {
1512
+ isChecked: [{ type: HostBinding, args: ["class.checked",] }]
881
1513
  };
882
1514
 
883
- class DynamicFormValidationService extends DynamicFormValidationService$1 {
884
- showErrorMessages(control, model, hasFocus) {
885
- return super.showErrorMessages(control, model, hasFocus);
1515
+ class DynamicFormSelectComponent extends FormControlComponent {
1516
+ // Acceptor for provider
1517
+ static acceptor(control) {
1518
+ return control.type == "select";
1519
+ }
1520
+ // Loader for provider
1521
+ static loader(control) {
1522
+ const data = control.getData();
1523
+ if (data.type == "radio" && data.multi) {
1524
+ return Promise.reject("Radio group doesn't support multi select!");
1525
+ }
1526
+ return new Promise(resolve => {
1527
+ const getOptions = ReflectUtils.resolve(data.options, control.form.injector);
1528
+ getOptions(control).then(options => {
1529
+ if (data.emptyOption)
1530
+ options.unshift({ id: null, label: "" });
1531
+ control.meta.options = options;
1532
+ DynamicFormSelectComponent.fillOptions(control, options);
1533
+ resolve(options);
1534
+ });
1535
+ });
1536
+ }
1537
+ static fillOptions(control, options) {
1538
+ const data = control.getData();
1539
+ const selected = control.value;
1540
+ if (data.multi || options.length == 0 || options.findIndex(t => t.id == selected) >= 0)
1541
+ return;
1542
+ control.setValue(options[0].id);
1543
+ }
1544
+ onSelectChange(value) {
1545
+ const isArray = ObjectUtils.isArray(value);
1546
+ const current = this.value;
1547
+ if (this.data.multi) {
1548
+ if (isArray) {
1549
+ this.control.setValue(value);
1550
+ return;
1551
+ }
1552
+ if (ObjectUtils.isArray(current)) {
1553
+ this.control.setValue(current.indexOf(value) < 0
1554
+ ? current.concat([value])
1555
+ : current.filter(c => c !== value));
1556
+ return;
1557
+ }
1558
+ this.control.setValue([value]);
1559
+ return;
1560
+ }
1561
+ if (isArray)
1562
+ value = value[0];
1563
+ if (current == value) {
1564
+ const option = this.meta.options.find(o => o.id !== value);
1565
+ value = option ? option.id : null;
1566
+ }
1567
+ this.control.setValue(value);
1568
+ }
1569
+ checkValue(option) {
1570
+ const value = this.value;
1571
+ return ObjectUtils.isArray(value) ? value.indexOf(option.id) >= 0 : option.id == value;
1572
+ }
1573
+ findOption(option, index, id) {
1574
+ return option.id == id;
1575
+ }
1576
+ }
1577
+ DynamicFormSelectComponent.decorators = [
1578
+ { type: Component, args: [{
1579
+ moduleId: module.id,
1580
+ selector: "dynamic-form-select",
1581
+ template: "<ng-template #selectTemplate let-selected=\"selected\">\r\n <select class=\"form-control\"\r\n [multiple]=\"data.multi\"\r\n [id]=\"control.formId\"\r\n [name]=\"control.formId\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled\"\r\n [ngModel]=\"data.multi ? value : [value]\"\r\n (ngModelChange)=\"onSelectChange($event)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\">\r\n <option *ngFor=\"let option of meta.options\" [value]=\"option.id\" [disabled]=\"option.selectable == false\">\r\n {{ option.label | translate }}\r\n </option>\r\n </select>\r\n <label class=\"form-control-description\" [attr.for]=\"control.formId\" *ngIf=\"!data.multi && selected?.description\">\r\n {{ selected?.description | translate }}\r\n </label>\r\n</ng-template>\r\n<ng-container [ngSwitch]=\"data.type\">\r\n <ng-container *ngSwitchCase=\"'radio'\">\r\n <ul class=\"radio-group\">\r\n <li *ngFor=\"let option of meta.options\" class=\"radio\">\r\n <label [ngClass]=\"{checked: value == option.id}\">\r\n <input type=\"radio\"\r\n [id]=\"control.formId + '-' + option.id\"\r\n [name]=\"control.formId + '-' + option.id\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled || option.selectable == false\"\r\n [value]=\"option.id\"\r\n [ngModel]=\"value\"\r\n (ngModelChange)=\"onSelectChange(option.id)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\">\r\n {{ option.label | translate }}\r\n </label>\r\n </li>\r\n </ul>\r\n </ng-container>\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <ul class=\"checkbox-group\">\r\n <li *ngFor=\"let option of meta.options\" class=\"checkbox\">\r\n <label [ngClass]=\"{checked: checkValue(option)}\">\r\n <input type=\"checkbox\"\r\n [id]=\"control.formId + '-' + option.id\"\r\n [name]=\"control.formId + '-' + option.id\"\r\n [ngClass]=\"{disabled: control.disabled}\"\r\n [disabled]=\"control.disabled || option.selectable == false\"\r\n [ngModel]=\"checkValue(option)\"\r\n (ngModelChange)=\"onSelectChange(option.id)\"\r\n (blur)=\"control.onBlur()\"\r\n (focus)=\"control.onFocus()\">\r\n {{ option.label | translate }}\r\n </label>\r\n </li>\r\n </ul>\r\n </ng-container>\r\n <ng-container *ngSwitchDefault [ngTemplateOutlet]=\"selectTemplate\" [ngTemplateOutletContext]=\"{selected: meta?.options | find:findOption:value}\">\r\n\r\n </ng-container>\r\n</ng-container>\r\n"
1582
+ },] }
1583
+ ];
1584
+
1585
+ class DynamicFormStaticComponent extends FormControlComponent {
1586
+ // Acceptor for provider
1587
+ static acceptor(control) {
1588
+ return control.type == "static";
1589
+ }
1590
+ // Loader for provider
1591
+ static loader() {
1592
+ return Promise.resolve();
886
1593
  }
887
1594
  }
888
- DynamicFormValidationService.decorators = [
889
- { type: Injectable }
1595
+ DynamicFormStaticComponent.decorators = [
1596
+ { type: Component, args: [{
1597
+ moduleId: module.id,
1598
+ selector: "dynamic-form-static",
1599
+ template: "<unordered-list [listStyle]=\"data.style\" [ngClass]=\"{disabled: control.disabled}\"\r\n [data]=\"!data.properties ? {value: value} : data.properties | remap: value\">\r\n <ng-template [type]=\"!data.properties ? 'key' : null\" selector=\"level == 0\" let-item=\"item\"></ng-template>\r\n <ng-template type=\"value\" selector=\"valueType == 'date'\" let-item=\"item\">\r\n {{ item.value | date }}\r\n </ng-template>\r\n</unordered-list>\r\n"
1600
+ },] }
890
1601
  ];
891
1602
 
1603
+ class DynamicFormModelComponent extends FormControlComponent {
1604
+ // Acceptor for provider
1605
+ static acceptor(control) {
1606
+ return control.type == "model";
1607
+ }
1608
+ // Loader for provider
1609
+ static loader() {
1610
+ return Promise.resolve();
1611
+ }
1612
+ }
1613
+ DynamicFormModelComponent.decorators = [
1614
+ { type: Component, args: [{
1615
+ moduleId: module.id,
1616
+ selector: "dynamic-form-model",
1617
+ template: "<dynamic-form [name]=\"data.name || form.name\"\r\n [group]=\"control\"\r\n [data]=\"value\"\r\n [updateOn]=\"control.updateOn\"\r\n [parent]=\"form\"\r\n\r\n [fieldSetTemplate]=\"form.fieldSetTemplate\"\r\n [controlTemplates]=\"form.controlTemplates\"\r\n\r\n [controlTemplate]=\"form.controlTemplate\"\r\n [labelTemplates]=\"form.labelTemplates\"\r\n [inputTemplates]=\"form.inputTemplates\"\r\n [prefixTemplates]=\"form.prefixTemplates\"\r\n [suffixTemplates]=\"form.suffixTemplates\"\r\n [setPrefixTemplates]=\"form.setPrefixTemplates\"\r\n [setSuffixTemplates]=\"form.setSuffixTemplates\"\r\n\r\n #subForm>\r\n <ng-template #wrapperTemplate let-form=\"form\" let-fieldSetsTemplate=\"fieldSetsTemplate\">\r\n <ng-container [ngTemplateOutlet]=\"fieldSetsTemplate\"></ng-container>\r\n </ng-template>\r\n</dynamic-form>\r\n"
1618
+ },] }
1619
+ ];
1620
+ DynamicFormModelComponent.propDecorators = {
1621
+ subForm: [{ type: ViewChild, args: ["subForm",] }]
1622
+ };
1623
+
1624
+ class DynamicFormGroupDirective {
1625
+ constructor(vcr, forms) {
1626
+ this.vcr = vcr;
1627
+ this.forms = forms;
1628
+ }
1629
+ get component() {
1630
+ return this.comp;
1631
+ }
1632
+ ngOnChanges(changes) {
1633
+ if (changes.control || changes.form || changes.visible) {
1634
+ if (!this.visible) {
1635
+ this.vcr.clear();
1636
+ this.comp = null;
1637
+ return;
1638
+ }
1639
+ this.comp = this.forms.createGroup(this.vcr);
1640
+ }
1641
+ if (!this.comp)
1642
+ return;
1643
+ this.comp.form = this.form;
1644
+ this.comp.control = this.control;
1645
+ }
1646
+ }
1647
+ DynamicFormGroupDirective.decorators = [
1648
+ { type: Directive, args: [{
1649
+ selector: "[form-group]",
1650
+ },] }
1651
+ ];
1652
+ DynamicFormGroupDirective.ctorParameters = () => [
1653
+ { type: ViewContainerRef },
1654
+ { type: DynamicFormService }
1655
+ ];
1656
+ DynamicFormGroupDirective.propDecorators = {
1657
+ control: [{ type: Input, args: ["form-group",] }],
1658
+ form: [{ type: Input }],
1659
+ visible: [{ type: Input }]
1660
+ };
1661
+
892
1662
  // --- Components ---
893
1663
  const components = [
894
- DynamicBaseFormComponent,
895
- DynamicBaseFormControlContainerComponent
1664
+ DynamicFormsComponent,
1665
+ DynamicFormComponent,
1666
+ DynamicFormGroupComponent,
1667
+ DynamicFormFileComponent,
1668
+ DynamicFormInputComponent,
1669
+ DynamicFormSelectComponent,
1670
+ DynamicFormStaticComponent,
1671
+ DynamicFormModelComponent
896
1672
  ];
897
1673
  // --- Directives ---
898
1674
  const directives = [
899
1675
  AsyncSubmitDirective,
1676
+ DynamicFormControlDirective,
1677
+ DynamicFormGroupDirective,
1678
+ DynamicFormTemplateDirective
900
1679
  ];
901
1680
  // --- Pipes ---
902
1681
  const pipes = [];
903
- const ɵ0$1 = validateJSON, ɵ1 = validateRequiredTranslation, ɵ2 = validatePhone, ɵ3 = new Map([
904
- ["validateJSON", validateJSON],
905
- ["validateRequiredTranslation", validateRequiredTranslation],
906
- ["validatePhone", validatePhone],
907
- ]);
908
1682
  class NgxDynamicFormModule {
909
- static forRoot() {
1683
+ static forRoot(controlProviders, groupProvider) {
910
1684
  return {
911
1685
  ngModule: NgxDynamicFormModule,
912
1686
  providers: [
913
1687
  DynamicFormService,
914
- DynamicFormValidationService,
915
- {
916
- provide: DynamicFormService$1,
917
- useExisting: DynamicFormService
918
- },
919
- {
920
- provide: DynamicFormValidationService$1,
921
- useExisting: DynamicFormValidationService
922
- }
1688
+ OpenApiService,
1689
+ provideFormControl(DynamicFormFileComponent, DynamicFormFileComponent.acceptor, DynamicFormFileComponent.loader),
1690
+ provideFormControl(DynamicFormInputComponent, DynamicFormInputComponent.acceptor, DynamicFormInputComponent.loader),
1691
+ provideFormControl(DynamicFormSelectComponent, DynamicFormSelectComponent.acceptor, DynamicFormSelectComponent.loader),
1692
+ provideFormControl(DynamicFormStaticComponent, DynamicFormStaticComponent.acceptor, DynamicFormStaticComponent.loader),
1693
+ provideFormControl(DynamicFormModelComponent, DynamicFormModelComponent.acceptor, DynamicFormModelComponent.loader),
1694
+ ...(controlProviders || []),
1695
+ groupProvider || provideFormGroup(DynamicFormGroupComponent)
923
1696
  ]
924
1697
  };
925
1698
  }
@@ -946,16 +1719,7 @@ NgxDynamicFormModule.decorators = [
946
1719
  NgxUtilsModule
947
1720
  ],
948
1721
  entryComponents: components,
949
- providers: [
950
- ...pipes,
951
- { provide: NG_VALIDATORS, useValue: ɵ0$1, multi: true },
952
- { provide: NG_VALIDATORS, useValue: ɵ1, multi: true },
953
- { provide: NG_VALIDATORS, useValue: ɵ2, multi: true },
954
- {
955
- provide: DYNAMIC_VALIDATORS,
956
- useValue: ɵ3
957
- }
958
- ]
1722
+ providers: pipes
959
1723
  },] }
960
1724
  ];
961
1725
 
@@ -963,5 +1727,5 @@ NgxDynamicFormModule.decorators = [
963
1727
  * Generated bundle index. Do not edit.
964
1728
  */
965
1729
 
966
- export { AsyncSubmitDirective, DynamicBaseFormComponent, DynamicBaseFormControlContainerComponent, DynamicFormService, FormFieldSet, FormFile, FormInput, FormModel, FormSelect, FormSerializable, FormStatic, FormSubject, NgxDynamicFormModule, createFormControl, createFormInput, createFormModel, createFormSelect, createFormStatic, defaultSerializer, defineFormControl, getFormControl, getFormFieldSets, getFormSerializer, validateJSON, validatePhone, validateRequiredTranslation, components as ɵa, directives as ɵb, pipes as ɵc, DynamicFormValidationService as ɵd };
1730
+ export { AsyncSubmitDirective, DynamicFormBaseComponent, DynamicFormComponent, DynamicFormControl, DynamicFormControlDirective, DynamicFormFileComponent, DynamicFormGroup, DynamicFormGroupComponent, DynamicFormInputComponent, DynamicFormModelComponent, DynamicFormSelectComponent, DynamicFormService, DynamicFormStaticComponent, DynamicFormTemplateDirective, DynamicFormsComponent, FORM_CONTROL_PROVIDER, FormControlComponent, FormFieldSet, FormFile, FormInput, FormModel, FormSelect, FormSerializable, FormStatic, FormUtilities, NgxDynamicFormModule, OpenApiService, createFormControl, createFormInput, createFormModel, createFormSelect, createFormStatic, defaultSerializer, defineFormControl, getFormControl, getFormFieldSets, getFormSerializer, provideFormControl, provideFormGroup, FORM_GROUP_TYPE as ɵa, components as ɵb, directives as ɵc, pipes as ɵd, DynamicFormGroupDirective as ɵe };
967
1731
  //# sourceMappingURL=stemy-ngx-dynamic-form.js.map