@stemy/ngx-dynamic-form 10.2.22 → 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 -902
  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 -386
  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 -594
  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,448 +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;
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
- ? parseFloat((`${subControl.value}` || "0").replace(/,/gi, ".")) || null
307
- : subControl.value;
308
- continue;
309
- }
310
- result[subModel.id] = subControl.value;
311
- }
312
- return result;
313
- });
665
+ static validateRequired(control) {
666
+ const value = control.value;
667
+ return Promise.resolve(!ObjectUtils.isNumber(value) && !value ? "error.required" : null);
314
668
  }
315
- notifyChangesRecursive(formModel, formGroup) {
316
- if (!formModel || !formGroup)
317
- return;
318
- for (const i in formModel) {
319
- const subModel = formModel[i];
320
- const subControl = this.findControlByModel(subModel, formGroup);
321
- if (subModel instanceof DynamicFormArrayModel$1) {
322
- const length = Array.isArray(subControl.value) ? subControl.value.length : 0;
323
- const subArray = subControl;
324
- for (let i = 0; i < length; i++) {
325
- const itemModel = subModel.get(i);
326
- this.notifyChangesRecursive(itemModel.group, subArray.at(i));
327
- }
328
- 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 } });
329
675
  }
330
- if (subModel instanceof DynamicFormGroupModel) {
331
- this.notifyChangesRecursive(subModel.group, subControl);
332
- 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 } });
333
686
  }
334
- this.updateSelectOptions(subModel, subControl);
335
- }
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;
336
738
  }
337
- showErrorsForGroup(formGroup) {
338
- if (!formGroup)
339
- return;
340
- formGroup.markAsTouched({ onlySelf: true });
341
- const controls = Object.keys(formGroup.controls).map(id => formGroup.controls[id]);
342
- this.showErrorsForControls(controls);
343
- }
344
- showErrorsForControls(controls) {
345
- controls.forEach(control => {
346
- if (control instanceof FormGroup) {
347
- this.showErrorsForGroup(control);
348
- return;
349
- }
350
- control.markAsTouched({ onlySelf: true });
351
- if (control instanceof FormArray) {
352
- this.showErrorsForControls(control.controls);
353
- }
354
- });
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
+ }));
355
757
  }
356
- getFormModelForSchema(name) {
357
- return __awaiter(this, void 0, void 0, function* () {
358
- this.api.cache = {};
359
- this.schemas = yield this.openApi.getSchemas();
360
- 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;
361
764
  });
362
765
  }
363
- getFormModelForSchemaDef(schema) {
766
+ getFormInputsForSchemaDef(schema) {
767
+ const inputs = {};
364
768
  if (!schema)
365
- return [];
366
- 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 => {
367
773
  const property = schema.properties[p];
368
- 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);
369
779
  }).filter(t => null !== t);
780
+ return inputs;
370
781
  }
371
- getFormControlModel(property, schema) {
372
- var _a, _b;
373
- if (Array.isArray(property.enum) || ObjectUtils.isString(property.optionsPath)) {
374
- 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));
375
785
  }
376
786
  switch (property.type) {
377
787
  case "string":
378
788
  case "number":
379
- case "integer":
380
- return new DynamicInputModel(this.getFormInputConfig(property, schema));
381
- case "textarea":
382
- return new DynamicTextAreaModel(this.getFormTextareaConfig(property, schema));
383
789
  case "boolean":
384
- return new DynamicCheckboxModel(this.getFormCheckboxConfig(property, schema));
790
+ case "textarea":
791
+ return createFormInput(property.id, this.getFormInputData(property, schema));
385
792
  case "list":
386
- return new DynamicSelectModel(this.getFormSelectConfig(property, schema));
387
- case "array":
388
- if (((_a = property.items) === null || _a === void 0 ? void 0 : _a.$ref) || property.$ref) {
389
- return new DynamicFormArrayModel(this.getFormArrayConfig(property, schema));
390
- }
391
- else if (((_b = property.items) === null || _b === void 0 ? void 0 : _b.enum) || property.enum) {
392
- return new DynamicSelectModel(this.getFormSelectConfig(property, schema));
393
- }
394
- else {
395
- return new DynamicInputModel(this.getFormInputConfig(property, schema));
396
- }
793
+ return createFormSelect(property.id, this.getFormSelectData(property, schema));
397
794
  case "file":
398
- return new DynamicFileUploadModel(this.getFormUploadConfig(property, schema));
399
- }
400
- if (property.$ref) {
401
- return new DynamicFormGroupModel(this.getFormGroupConfig(property, schema));
795
+ return createFormFile(property.id, this.getFormFileData(property, schema));
402
796
  }
403
797
  return null;
404
798
  }
405
- getFormControlConfig(property, schema) {
406
- const validators = this.getValidators(property, schema);
407
- const errorMessages = Object.keys(validators).reduce((res, key) => {
408
- res[key] = `error.${key}`;
409
- return res;
410
- }, {});
799
+ getBaseFormData(property, schema) {
411
800
  return {
412
- id: property.id,
413
- label: ObjectUtils.isString(property.label) ? property.label : property.id,
414
- hidden: property.hidden,
415
- disabled: property.disabled,
416
- validators,
417
- errorMessages,
418
- additional: Object.assign({}, property)
801
+ readonly: property.readonly ? FormUtilities.readonly : null,
802
+ shouldSerialize: FormUtilities.checkReadonly,
803
+ validators: this.getValidators(property, schema)
419
804
  };
420
805
  }
421
- getFormArrayConfig(property, schema) {
422
- var _a;
423
- const ref = ((_a = property.items) === null || _a === void 0 ? void 0 : _a.$ref) || property.$ref || "";
424
- const subSchema = this.schemas[ref.split("/").pop()];
425
- return Object.assign(this.getFormControlConfig(property, schema), { groupFactory: () => this.getFormModelForSchemaDef(subSchema) });
426
- }
427
- getFormGroupConfig(property, schema) {
428
- const ref = property.$ref || "";
429
- const subSchema = this.schemas[ref.split("/").pop()];
430
- return Object.assign(this.getFormControlConfig(property, schema), { group: this.getFormModelForSchemaDef(subSchema) });
431
- }
432
- getFormInputConfig(property, schema) {
433
- var _a;
434
- let inputType = StringUtils.has(property.id, "password", "Password") ? "password" : (((_a = property.items) === null || _a === void 0 ? void 0 : _a.type) || property.type);
435
- switch (inputType) {
806
+ getFormInputData(property, schema) {
807
+ let type = StringUtils.has(property.id, "password", "Password") ? "password" : property.type;
808
+ switch (property.type) {
436
809
  case "boolean":
437
- inputType = "checkbox";
810
+ type = "checkbox";
438
811
  break;
439
812
  case "textarea":
440
- inputType = "textarea";
441
- break;
442
- case "integer":
443
- inputType = "number";
813
+ type = "textarea";
444
814
  break;
445
815
  }
446
- return Object.assign(this.getFormControlConfig(property, schema), {
447
- inputType,
448
- autoComplete: property.autoComplete || "off",
449
- multiple: property.type == "array",
450
- accept: ObjectUtils.isString(property.accept) ? property.accept : null,
451
- mask: ObjectUtils.isString(property.mask) ? property.mask : null,
452
- pattern: ObjectUtils.isString(property.pattern) ? property.pattern : null,
453
- step: isNaN(property.step) ? 1 : property.step,
454
- min: isNaN(property.min) ? Number.MIN_SAFE_INTEGER : property.min,
455
- max: isNaN(property.max) ? Number.MAX_SAFE_INTEGER : property.max,
456
- });
457
- }
458
- getFormTextareaConfig(property, schema) {
459
- return Object.assign(this.getFormControlConfig(property, schema), {
460
- cols: property.cols || null,
461
- rows: property.rows || 10,
462
- wrap: property.wrap || false,
463
- autoComplete: property.autoComplete || "off",
464
- multiple: property.type == "array"
465
- });
816
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { type });
466
817
  }
467
- getFormSelectConfig(property, schema) {
468
- return Object.assign(this.getFormControlConfig(property, schema), {
469
- options: this.getFormSelectOptions(property, schema),
470
- multiple: property.type == "array"
471
- });
472
- }
473
- getFormCheckboxConfig(property, schema) {
474
- return Object.assign(this.getFormControlConfig(property, schema), {
475
- indeterminate: property.indeterminate === true
476
- });
477
- }
478
- translateOptions(options) {
479
- return __awaiter(this, void 0, void 0, function* () {
480
- if (!options)
481
- return [];
482
- for (const option of options) {
483
- 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}` })));
484
822
  }
485
- return options;
486
- });
487
- }
488
- getFormSelectOptions(property, schema) {
489
- var _a;
490
- const $enum = ((_a = property.items) === null || _a === void 0 ? void 0 : _a.enum) || property.enum;
491
- if (property.optionsPath) {
492
- return new FormSubject((formModel, control) => {
493
- let path = property.optionsPath;
494
- let target = control;
495
- if (path.startsWith("$root")) {
496
- path = path.substr(5);
497
- while (target.parent) {
498
- target = target.parent;
499
- }
500
- }
501
- while (path.startsWith(".")) {
502
- path = path.substr(1);
503
- if (target.parent) {
504
- target = target.parent;
505
- }
506
- }
507
- const value = ObjectUtils.getValue(target.value, path);
508
- const options = (!ObjectUtils.isArray(value) ? [] : value).map(value => ({ value, label: value }));
509
- return this.translateOptions(options);
510
- });
511
- }
512
- if (ObjectUtils.isArray($enum)) {
513
- return new FormSubject(() => {
514
- const options = $enum.map(value => {
515
- const label = property.translatable ? `${property.id}.${value}` : value;
516
- return { value, label };
517
- });
518
- return this.translateOptions(options);
519
- });
520
- }
521
- return new FormSubject(() => __awaiter(this, void 0, void 0, function* () {
522
- this.api.cache[property.endpoint] = this.api.cache[property.endpoint] || this.api.list(property.endpoint, this.api.makeListParams(1, -1)).then(result => {
523
- return result.items.map(i => {
524
- 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
+ });
525
828
  });
526
- });
527
- const options = (yield this.api.cache[property.endpoint]).map(t => Object.assign({}, t));
528
- return this.translateOptions(options);
529
- }));
829
+ return this.api.cache[property.endpoint];
830
+ };
831
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { options });
530
832
  }
531
- getFormUploadConfig(property, schema) {
532
- const url = this.api.url(property.url || "assets");
533
- const { accept, autoUpload, maxSize, minSize, removeUrl, showFileList } = property;
534
- return Object.assign(this.getFormControlConfig(property, schema), {
535
- url,
536
- accept,
537
- autoUpload,
538
- maxSize,
539
- minSize,
540
- removeUrl,
541
- showFileList
542
- });
833
+ getFormFileData(property, schema) {
834
+ return Object.assign(Object.assign({}, this.getBaseFormData(property, schema)), { multi: property.multi });
543
835
  }
544
836
  getValidators(property, schema) {
545
- const validators = {};
546
- if (ObjectUtils.isArray(schema.required) && schema.required.indexOf(property.id) >= 0) {
547
- validators.required = null;
837
+ const validators = [];
838
+ if (schema.required.indexOf(property.id) >= 0) {
839
+ validators.push(FormUtilities.validateRequired);
548
840
  }
549
841
  if (property.minLength) {
550
- validators.minLength = property.minLength;
842
+ validators.push({
843
+ type: FormUtilities,
844
+ func: FormUtilities.validateMinLength,
845
+ params: [property.minLength]
846
+ });
551
847
  }
552
848
  if (property.maxLength) {
553
- validators.maxLength = property.maxLength;
554
- }
555
- if (property.min) {
556
- validators.min = property.min;
557
- }
558
- if (property.max) {
559
- validators.max = property.max;
849
+ validators.push({
850
+ type: FormUtilities,
851
+ func: FormUtilities.validateMaxLength,
852
+ params: [property.maxLength]
853
+ });
560
854
  }
561
855
  switch (property.format) {
562
856
  case "email":
563
- validators.email = null;
857
+ validators.push(FormUtilities.validateEmail);
564
858
  break;
565
859
  }
566
860
  return validators;
567
861
  }
568
862
  }
569
- DynamicFormService.decorators = [
863
+ OpenApiService.decorators = [
570
864
  { type: Injectable }
571
865
  ];
572
- DynamicFormService.ctorParameters = () => [
573
- { type: DynamicFormComponentService },
574
- { type: DynamicFormValidationService$1 },
575
- { type: OpenApiService }
866
+ OpenApiService.ctorParameters = () => [
867
+ { type: ApiService }
576
868
  ];
577
869
 
578
870
  class AsyncSubmitDirective {
579
871
  constructor(toaster, cdr, elem, renderer) {
580
872
  this.toaster = toaster;
581
873
  this.cdr = cdr;
582
- this.elem = elem;
583
- this.renderer = renderer;
584
874
  this.onSuccess = new EventEmitter();
585
875
  this.onError = new EventEmitter();
586
876
  if (elem.nativeElement.tagName !== "BUTTON")
@@ -590,26 +880,18 @@ class AsyncSubmitDirective {
590
880
  get isDisabled() {
591
881
  return this.disabled;
592
882
  }
593
- set isDisabled(value) {
594
- this.disabled = value;
595
- if (value) {
596
- this.renderer.setAttribute(this.elem.nativeElement, "disabled", "disabled");
597
- return;
598
- }
599
- this.renderer.removeAttribute(this.elem.nativeElement, "disabled");
600
- }
601
883
  get isLoading() {
602
884
  return this.loading;
603
885
  }
604
886
  ngOnInit() {
605
887
  if (!this.form)
606
888
  return;
607
- this.isDisabled = this.form.status !== "VALID";
889
+ this.disabled = this.form.status !== "VALID";
608
890
  this.cdr.detectChanges();
609
- this.onStatusChange = this.form.onStatusChange.subscribe(() => {
610
- this.isDisabled = this.form.status !== "VALID";
891
+ this.onStatusChange = this.form.onStatusChange.subscribe(form => {
892
+ this.disabled = form.status !== "VALID";
611
893
  this.cdr.detectChanges();
612
- if (!this.callback || this.form.status == "PENDING")
894
+ if (!this.callback || form.status == "PENDING")
613
895
  return;
614
896
  if (!this.disabled) {
615
897
  this.callback();
@@ -635,7 +917,7 @@ class AsyncSubmitDirective {
635
917
  if (this.loading)
636
918
  return;
637
919
  this.loading = true;
638
- this.method(this.form, this.context).then(result => {
920
+ this.method(this.form).then(result => {
639
921
  this.loading = false;
640
922
  if (result) {
641
923
  this.onSuccess.emit(result);
@@ -665,7 +947,6 @@ AsyncSubmitDirective.ctorParameters = () => [
665
947
  AsyncSubmitDirective.propDecorators = {
666
948
  method: [{ type: Input, args: ["async-submit",] }],
667
949
  form: [{ type: Input }],
668
- context: [{ type: Input }],
669
950
  onSuccess: [{ type: Output }],
670
951
  onError: [{ type: Output }],
671
952
  isDisabled: [{ type: HostBinding, args: ["class.disabled",] }],
@@ -673,78 +954,194 @@ AsyncSubmitDirective.propDecorators = {
673
954
  click: [{ type: HostListener, args: ["click",] }]
674
955
  };
675
956
 
676
- class DynamicBaseFormComponent extends DynamicFormComponent {
677
- constructor(formService, events, changeDetectorRef, componentService) {
678
- 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) {
679
1026
  this.formService = formService;
680
- this.events = events;
681
- this.blur = new EventEmitter();
682
- this.change = new EventEmitter();
683
- 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();
684
1034
  this.onStatusChange = new EventEmitter();
685
- this.onValueChange = new EventEmitter();
1035
+ this.onInit = new EventEmitter();
686
1036
  this.onSubmit = new EventEmitter();
687
- this.templates = new QueryList();
688
- this.subscription = new Subscription();
689
- this.groupSubscription = new Subscription();
690
- this.labelPrefix = "label";
691
- }
692
- get status() {
693
- return !this.group ? null : this.group.status;
1037
+ this.injector = formService.injector;
1038
+ this.cdr = cdr;
694
1039
  }
695
- ngOnChanges(changes) {
696
- this.groupSubscription.unsubscribe();
697
- if (this.group) {
698
- this.groupSubscription = ObservableUtils.multiSubscription(this.group.statusChanges.subscribe(() => {
699
- this.onStatusChange.emit(this);
700
- }), this.group.valueChanges.subscribe(() => {
701
- this.onValueChange.emit(this);
702
- this.formService.notifyChanges(this.model, this.group);
703
- }));
1040
+ get root() {
1041
+ let form = this;
1042
+ while (ObjectUtils.isDefined(form.parent)) {
1043
+ form = form.parent;
704
1044
  }
1045
+ return form;
705
1046
  }
706
- ngAfterViewInit() {
707
- this.subscription = ObservableUtils.multiSubscription(ObservableUtils.subscribe({
708
- subjects: [this.contentTemplates.changes, this.viewTemplates.changes],
709
- cb: () => {
710
- const templates = this.contentTemplates.toArray().concat(this.viewTemplates.toArray());
711
- this.templates.reset(templates);
712
- }
713
- }), this.events.languageChanged.subscribe(() => {
714
- this.formService.detectChanges(this);
715
- }), this.ngForm.ngSubmit.subscribe(() => {
716
- this.onSubmit.emit(this);
717
- }));
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
+ }, {});
718
1070
  }
719
- ngOnDestroy() {
720
- super.ngOnDestroy();
721
- this.subscription.unsubscribe();
722
- 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
+ };
723
1116
  }
724
- insertFormArrayGroup(index, formArray, formArrayModel) {
725
- this.formService.insertFormArrayGroup(index, formArray, formArrayModel);
726
- this.changeDetectorRef.detectChanges();
1117
+ get status() {
1118
+ return this.group.state;
727
1119
  }
728
- removeFormArrayGroup(index, formArray, formArrayModel) {
729
- this.formService.removeFormArrayGroup(index, formArray, formArrayModel);
730
- this.changeDetectorRef.detectChanges();
1120
+ get formControls() {
1121
+ return this.group.formControls;
731
1122
  }
732
- moveFormArrayGroup(index, step, formArray, formArrayModel) {
733
- this.formService.moveFormArrayGroup(index, step, formArray, formArrayModel);
734
- 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
+ }
735
1133
  }
736
- clearFormArray(formArray, formArrayModel) {
737
- this.formService.clearFormArray(formArray, formArrayModel);
738
- this.changeDetectorRef.detectChanges();
1134
+ // --- Custom ---
1135
+ onFormSubmit() {
1136
+ const root = this.root;
1137
+ root.validate().then(() => root.onSubmit.emit(this), () => { });
739
1138
  }
1139
+ // --- IDynamicForm ---
740
1140
  validate(showErrors = true) {
741
- if (!this.group)
742
- return Promise.resolve();
743
1141
  return new Promise((resolve, reject) => {
744
1142
  this.group.statusChanges.pipe(first(status => status == "VALID" || status == "INVALID")).subscribe(status => {
745
- if (showErrors) {
746
- this.formService.showErrors(this);
747
- }
1143
+ if (showErrors)
1144
+ this.group.showErrors();
748
1145
  if (status == "VALID") {
749
1146
  resolve(null);
750
1147
  return;
@@ -755,172 +1152,547 @@ class DynamicBaseFormComponent extends DynamicFormComponent {
755
1152
  });
756
1153
  }
757
1154
  serialize(validate) {
758
- return __awaiter(this, void 0, void 0, function* () {
759
- if (!this.group)
760
- return null;
1155
+ return new Promise((resolve, reject) => {
1156
+ const serialize = () => {
1157
+ this.group.serialize().then(resolve);
1158
+ };
761
1159
  if (validate) {
762
- yield this.validate();
1160
+ this.validate().then(serialize, reject);
1161
+ return;
763
1162
  }
764
- return yield this.formService.serialize(this.model, this.group);
1163
+ serialize();
765
1164
  });
766
1165
  }
1166
+ check() {
1167
+ return this.group.check();
1168
+ }
1169
+ getControl(id) {
1170
+ return this.group.getControl(id);
1171
+ }
767
1172
  }
768
- DynamicBaseFormComponent.decorators = [
1173
+ DynamicFormComponent.decorators = [
769
1174
  { type: Component, args: [{
770
- selector: "dynamic-base-form",
771
- template: "",
772
- 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 }]
773
1179
  },] }
774
1180
  ];
775
- DynamicBaseFormComponent.ctorParameters = () => [
776
- { type: DynamicFormService, decorators: [{ type: Inject, args: [DynamicFormService,] }] },
777
- { type: EventsService, decorators: [{ type: Inject, args: [EventsService,] }] },
1181
+ DynamicFormComponent.ctorParameters = () => [
778
1182
  { type: ChangeDetectorRef },
779
- { type: DynamicFormComponentService }
1183
+ { type: DynamicFormService }
780
1184
  ];
781
- DynamicBaseFormComponent.propDecorators = {
1185
+ DynamicFormComponent.propDecorators = {
782
1186
  group: [{ type: Input }],
783
- model: [{ type: Input }],
784
- layout: [{ type: Input }],
785
- labelPrefix: [{ type: Input }],
786
- blur: [{ type: Output }],
787
- change: [{ type: Output }],
788
- focus: [{ type: Output }],
789
- contentTemplates: [{ type: ContentChildren, args: [DynamicTemplateDirective,] }],
790
- viewTemplates: [{ type: ViewChildren, args: [DynamicTemplateDirective,] }],
791
- onStatusChange: [{ type: Output }],
792
- onValueChange: [{ type: Output }],
793
- onSubmit: [{ type: Output }],
794
- ngForm: [{ type: ViewChild, args: [NgForm,] }]
1187
+ controls: [{ type: Input }],
1188
+ serializers: [{ type: Input }],
1189
+ fieldSets: [{ type: Input }],
1190
+ data: [{ type: Input }]
795
1191
  };
796
1192
 
797
- class DynamicBaseFormControlContainerComponent extends DynamicFormControlContainerComponent {
798
- constructor(form, changeDetectorRef, componentFactoryResolver, layoutService, validationService, componentService, relationService) {
799
- super(changeDetectorRef, componentFactoryResolver, layoutService, validationService, componentService, relationService);
800
- this.form = form;
801
- this.changeDetectorRef = changeDetectorRef;
802
- this.componentFactoryResolver = componentFactoryResolver;
803
- this.layoutService = layoutService;
804
- this.validationService = validationService;
805
- this.componentService = componentService;
806
- this.relationService = relationService;
807
- this.context = null;
808
- this.blur = new EventEmitter();
809
- this.change = new EventEmitter();
810
- this.focus = new EventEmitter();
811
- }
812
- get componentType() {
813
- var _a;
814
- return (_a = this.componentService.getCustomComponentType(this.model)) !== null && _a !== void 0 ? _a : this.getComponentType(this.model);
815
- }
816
- get startTemplate() {
817
- return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
818
- ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_START")
819
- : this.layoutService.getStartTemplate(this.model, this.templates);
820
- }
821
- get endTemplate() {
822
- return (this.model.type == DYNAMIC_FORM_CONTROL_TYPE_ARRAY)
823
- ? this.layoutService.getAlignedTemplate(this.model, this.templates, "ARRAY_END")
824
- : this.layoutService.getEndTemplate(this.model, this.templates);
825
- }
826
- get formService() {
827
- 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 = {};
828
1201
  }
829
- ngOnInit() {
830
- this.onDetectChanges = this.formService.onDetectChanges.subscribe(form => {
831
- 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));
832
1391
  return;
833
- this.changeDetectorRef.detectChanges();
834
- 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)));
835
1402
  });
836
1403
  }
837
- ngOnDestroy() {
838
- super.ngOnDestroy();
839
- this.onDetectChanges.unsubscribe();
840
- }
841
- createFormControlComponent() {
842
- var _a;
843
- super.createFormControlComponent();
844
- const component = (_a = this.componentRef) === null || _a === void 0 ? void 0 : _a.instance;
845
- 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);
846
1409
  return;
847
- component.onCreated();
1410
+ }
1411
+ this.control.setValue(null);
848
1412
  }
849
- getComponentType(model) {
850
- 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}`);
851
1435
  }
852
1436
  }
853
- DynamicBaseFormControlContainerComponent.decorators = [
1437
+ DynamicFormFileComponent.decorators = [
854
1438
  { type: Component, args: [{
855
- selector: "dynamic-base-form-control",
856
- template: "",
857
- 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"]
858
1442
  },] }
859
1443
  ];
860
- DynamicBaseFormControlContainerComponent.ctorParameters = () => [
861
- { type: DynamicBaseFormComponent },
862
- { type: ChangeDetectorRef },
863
- { type: ComponentFactoryResolver },
864
- { type: DynamicFormLayoutService },
865
- { type: DynamicFormValidationService$1 },
866
- { type: DynamicFormComponentService },
867
- { 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
+ },] }
868
1507
  ];
869
- DynamicBaseFormControlContainerComponent.propDecorators = {
870
- contentTemplateList: [{ type: ContentChildren, args: [DynamicTemplateDirective,] }],
871
- klass: [{ type: HostBinding, args: ["class",] }],
872
- context: [{ type: Input }],
873
- group: [{ type: Input }],
874
- hostClass: [{ type: Input }],
875
- inputTemplateList: [{ type: Input, args: ["templates",] }],
876
- layout: [{ type: Input }],
877
- model: [{ type: Input }],
878
- blur: [{ type: Output }],
879
- change: [{ type: Output }],
880
- focus: [{ type: Output }],
881
- 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",] }]
882
1513
  };
883
1514
 
884
- class DynamicFormValidationService extends DynamicFormValidationService$1 {
885
- showErrorMessages(control, model, hasFocus) {
886
- 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();
887
1593
  }
888
1594
  }
889
- DynamicFormValidationService.decorators = [
890
- { 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
+ },] }
891
1601
  ];
892
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
+
893
1662
  // --- Components ---
894
1663
  const components = [
895
- DynamicBaseFormComponent,
896
- DynamicBaseFormControlContainerComponent
1664
+ DynamicFormsComponent,
1665
+ DynamicFormComponent,
1666
+ DynamicFormGroupComponent,
1667
+ DynamicFormFileComponent,
1668
+ DynamicFormInputComponent,
1669
+ DynamicFormSelectComponent,
1670
+ DynamicFormStaticComponent,
1671
+ DynamicFormModelComponent
897
1672
  ];
898
1673
  // --- Directives ---
899
1674
  const directives = [
900
1675
  AsyncSubmitDirective,
1676
+ DynamicFormControlDirective,
1677
+ DynamicFormGroupDirective,
1678
+ DynamicFormTemplateDirective
901
1679
  ];
902
1680
  // --- Pipes ---
903
1681
  const pipes = [];
904
- const ɵ0$1 = validateJSON, ɵ1 = validateRequiredTranslation, ɵ2 = validatePhone, ɵ3 = new Map([
905
- ["validateJSON", validateJSON],
906
- ["validateRequiredTranslation", validateRequiredTranslation],
907
- ["validatePhone", validatePhone],
908
- ]);
909
1682
  class NgxDynamicFormModule {
910
- static forRoot() {
1683
+ static forRoot(controlProviders, groupProvider) {
911
1684
  return {
912
1685
  ngModule: NgxDynamicFormModule,
913
1686
  providers: [
914
1687
  DynamicFormService,
915
- DynamicFormValidationService,
916
- {
917
- provide: DynamicFormService$1,
918
- useExisting: DynamicFormService
919
- },
920
- {
921
- provide: DynamicFormValidationService$1,
922
- useExisting: DynamicFormValidationService
923
- }
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)
924
1696
  ]
925
1697
  };
926
1698
  }
@@ -947,16 +1719,7 @@ NgxDynamicFormModule.decorators = [
947
1719
  NgxUtilsModule
948
1720
  ],
949
1721
  entryComponents: components,
950
- providers: [
951
- ...pipes,
952
- { provide: NG_VALIDATORS, useValue: ɵ0$1, multi: true },
953
- { provide: NG_VALIDATORS, useValue: ɵ1, multi: true },
954
- { provide: NG_VALIDATORS, useValue: ɵ2, multi: true },
955
- {
956
- provide: DYNAMIC_VALIDATORS,
957
- useValue: ɵ3
958
- }
959
- ]
1722
+ providers: pipes
960
1723
  },] }
961
1724
  ];
962
1725
 
@@ -964,5 +1727,5 @@ NgxDynamicFormModule.decorators = [
964
1727
  * Generated bundle index. Do not edit.
965
1728
  */
966
1729
 
967
- 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 };
968
1731
  //# sourceMappingURL=stemy-ngx-dynamic-form.js.map