@decaf-ts/for-angular 0.0.8 → 0.0.10

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 (105) hide show
  1. package/dist/{lib/assets → assets}/i18n/en.json +10 -1
  2. package/dist/components/empty-state/empty-state.component.d.ts +301 -0
  3. package/dist/components/fieldset/fieldset.component.d.ts +199 -0
  4. package/dist/components/filter/filter.component.d.ts +505 -0
  5. package/dist/components/for-angular-components.module.d.ts +20 -0
  6. package/dist/components/index.d.ts +16 -0
  7. package/dist/components/layout/layout.component.d.ts +133 -0
  8. package/dist/components/list/constants.d.ts +29 -0
  9. package/dist/components/list/list.component.d.ts +849 -0
  10. package/dist/components/list-item/list-item.component.d.ts +390 -0
  11. package/dist/{lib/components → components}/model-renderer/model-renderer.component.d.ts +1 -1
  12. package/dist/components/pagination/constants.d.ts +7 -0
  13. package/dist/components/pagination/pagination.component.d.ts +264 -0
  14. package/dist/components/searchbar/searchbar.component.d.ts +407 -0
  15. package/dist/directives/collapsable.directive.d.ts +8 -0
  16. package/dist/directives/index.d.ts +1 -0
  17. package/dist/engine/NgxBaseComponent.d.ts +541 -0
  18. package/dist/{lib/engine → engine}/index.d.ts +1 -0
  19. package/dist/{lib/engine → engine}/types.d.ts +44 -0
  20. package/dist/{lib/esm2022 → esm2022}/components/component-renderer/component-renderer.component.mjs +3 -3
  21. package/dist/esm2022/components/crud-field/crud-field.component.mjs +301 -0
  22. package/dist/esm2022/components/crud-form/constants.mjs +14 -0
  23. package/dist/esm2022/components/crud-form/crud-form.component.mjs +139 -0
  24. package/dist/esm2022/components/crud-form/types.mjs +2 -0
  25. package/dist/esm2022/components/empty-state/empty-state.component.mjs +348 -0
  26. package/dist/esm2022/components/fieldset/fieldset.component.mjs +225 -0
  27. package/dist/esm2022/components/filter/filter.component.mjs +689 -0
  28. package/dist/esm2022/components/for-angular-components.module.mjs +71 -0
  29. package/dist/esm2022/components/index.mjs +20 -0
  30. package/dist/esm2022/components/layout/layout.component.mjs +176 -0
  31. package/dist/esm2022/components/list/constants.mjs +6 -0
  32. package/dist/esm2022/components/list/list.component.mjs +1236 -0
  33. package/dist/esm2022/components/list-item/list-item.component.mjs +408 -0
  34. package/dist/esm2022/components/model-renderer/model-renderer.component.mjs +138 -0
  35. package/dist/esm2022/components/pagination/constants.mjs +2 -0
  36. package/dist/esm2022/components/pagination/pagination.component.mjs +323 -0
  37. package/dist/esm2022/components/searchbar/searchbar.component.mjs +493 -0
  38. package/dist/esm2022/decaf-ts-for-angular.mjs +5 -0
  39. package/dist/esm2022/directives/collapsable.directive.mjs +28 -0
  40. package/dist/esm2022/directives/index.mjs +2 -0
  41. package/dist/esm2022/engine/DynamicModule.mjs +18 -0
  42. package/dist/esm2022/engine/NgxBaseComponent.mjs +539 -0
  43. package/dist/esm2022/engine/NgxCrudFormField.mjs +125 -0
  44. package/dist/esm2022/engine/NgxFormService.mjs +315 -0
  45. package/dist/esm2022/engine/NgxRenderingEngine.mjs +192 -0
  46. package/dist/esm2022/engine/NgxRenderingEngine2.mjs +332 -0
  47. package/dist/esm2022/engine/ValidatorFactory.mjs +102 -0
  48. package/dist/esm2022/engine/constants.mjs +160 -0
  49. package/dist/esm2022/engine/decorators.mjs +38 -0
  50. package/dist/esm2022/engine/index.mjs +17 -0
  51. package/dist/esm2022/engine/types.mjs +4 -0
  52. package/dist/esm2022/for-angular.module.mjs +118 -0
  53. package/dist/esm2022/helpers/index.mjs +13 -0
  54. package/dist/esm2022/helpers/utils.mjs +415 -0
  55. package/dist/esm2022/interfaces.mjs +2 -0
  56. package/dist/esm2022/public-apis.mjs +14 -0
  57. package/dist/fesm2022/decaf-ts-for-angular.mjs +7109 -0
  58. package/dist/fesm2022/decaf-ts-for-angular.mjs.map +1 -0
  59. package/dist/helpers/index.d.ts +12 -0
  60. package/dist/helpers/utils.d.ts +253 -0
  61. package/dist/{lib/public-apis.d.ts → public-apis.d.ts} +4 -3
  62. package/package.json +5 -3
  63. package/dist/lib/esm2022/components/crud-field/crud-field.component.mjs +0 -297
  64. package/dist/lib/esm2022/components/crud-form/constants.mjs +0 -14
  65. package/dist/lib/esm2022/components/crud-form/crud-form.component.mjs +0 -139
  66. package/dist/lib/esm2022/components/crud-form/types.mjs +0 -2
  67. package/dist/lib/esm2022/components/model-renderer/model-renderer.component.mjs +0 -138
  68. package/dist/lib/esm2022/decaf-ts-for-angular.mjs +0 -5
  69. package/dist/lib/esm2022/engine/DynamicModule.mjs +0 -18
  70. package/dist/lib/esm2022/engine/NgxCrudFormField.mjs +0 -123
  71. package/dist/lib/esm2022/engine/NgxFormService.mjs +0 -315
  72. package/dist/lib/esm2022/engine/NgxRenderingEngine.mjs +0 -192
  73. package/dist/lib/esm2022/engine/NgxRenderingEngine2.mjs +0 -332
  74. package/dist/lib/esm2022/engine/ValidatorFactory.mjs +0 -102
  75. package/dist/lib/esm2022/engine/constants.mjs +0 -160
  76. package/dist/lib/esm2022/engine/decorators.mjs +0 -38
  77. package/dist/lib/esm2022/engine/index.mjs +0 -16
  78. package/dist/lib/esm2022/engine/types.mjs +0 -2
  79. package/dist/lib/esm2022/for-angular.module.mjs +0 -118
  80. package/dist/lib/esm2022/interfaces.mjs +0 -2
  81. package/dist/lib/esm2022/public-apis.mjs +0 -13
  82. package/dist/lib/fesm2022/decaf-ts-for-angular.mjs +0 -2153
  83. package/dist/lib/fesm2022/decaf-ts-for-angular.mjs.map +0 -1
  84. /package/dist/{lib/README.md → README.md} +0 -0
  85. /package/dist/{lib/assets → assets}/images/angular-logo.svg +0 -0
  86. /package/dist/{lib/assets → assets}/images/decaf-logo-black.svg +0 -0
  87. /package/dist/{lib/assets → assets}/images/decaf-logo-lw.svg +0 -0
  88. /package/dist/{lib/assets → assets}/images/decaf-logo-white.svg +0 -0
  89. /package/dist/{lib/assets → assets}/images/decaf-logo.svg +0 -0
  90. /package/dist/{lib/components → components}/component-renderer/component-renderer.component.d.ts +0 -0
  91. /package/dist/{lib/components → components}/crud-field/crud-field.component.d.ts +0 -0
  92. /package/dist/{lib/components → components}/crud-form/constants.d.ts +0 -0
  93. /package/dist/{lib/components → components}/crud-form/crud-form.component.d.ts +0 -0
  94. /package/dist/{lib/components → components}/crud-form/types.d.ts +0 -0
  95. /package/dist/{lib/engine → engine}/DynamicModule.d.ts +0 -0
  96. /package/dist/{lib/engine → engine}/NgxCrudFormField.d.ts +0 -0
  97. /package/dist/{lib/engine → engine}/NgxFormService.d.ts +0 -0
  98. /package/dist/{lib/engine → engine}/NgxRenderingEngine.d.ts +0 -0
  99. /package/dist/{lib/engine → engine}/NgxRenderingEngine2.d.ts +0 -0
  100. /package/dist/{lib/engine → engine}/ValidatorFactory.d.ts +0 -0
  101. /package/dist/{lib/engine → engine}/constants.d.ts +0 -0
  102. /package/dist/{lib/engine → engine}/decorators.d.ts +0 -0
  103. /package/dist/{lib/for-angular.module.d.ts → for-angular.module.d.ts} +0 -0
  104. /package/dist/{lib/index.d.ts → index.d.ts} +0 -0
  105. /package/dist/{lib/interfaces.d.ts → interfaces.d.ts} +0 -0
@@ -1,2153 +0,0 @@
1
- import { UIKeys, parseValueByType, HTML5InputTypes, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, RenderingError } from '@decaf-ts/ui-decorators';
2
- import * as i0 from '@angular/core';
3
- import { reflectComponentType, inject, EnvironmentInjector, EventEmitter, ViewContainerRef, TemplateRef, ViewChild, Input, Output, Component, Injector, ElementRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
4
- import { VALIDATION_PARENT_KEY, ValidationKeys, DEFAULT_PATTERNS, Validation, ComparisonValidationKeys, PathProxyEngine, isValidDate, parseDate, Model, sf } from '@decaf-ts/decorator-validation';
5
- import * as i1 from '@angular/common';
6
- import { NgComponentOutlet, Location } from '@angular/common';
7
- import { apply, metadata } from '@decaf-ts/reflection';
8
- import { InternalError, OperationKeys } from '@decaf-ts/db-decorators';
9
- import * as i2 from '@angular/forms';
10
- import { FormGroup, FormControl, Validators } from '@angular/forms';
11
- import { getLogger, ForAngularModule } from 'src/lib/for-angular.module';
12
- import { NgxRenderingEngine2 as NgxRenderingEngine2$1 } from 'src/lib/engine/NgxRenderingEngine2';
13
- import { __decorate } from 'tslib';
14
- import * as i3 from '@ngx-translate/core';
15
- import { TranslateService } from '@ngx-translate/core';
16
- import * as i1$1 from '@ionic/angular/standalone';
17
- import { IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonTextarea, IonText, IonIcon } from '@ionic/angular/standalone';
18
-
19
- /**
20
- * @description Angular engine key constants
21
- * @summary Contains key strings used by the Angular rendering engine for reflection,
22
- * dynamic component creation, and other engine operations.
23
- * @typedef {Object} AngularEngineKeys
24
- * @property {string} REFLECT - Prefix for reflection metadata keys
25
- * @property {string} DYNAMIC - Key for dynamic component identification
26
- * @property {string} ANNOTATIONS - Key for component annotations
27
- * @property {string} ECMP - Key for embedded components
28
- * @property {string} NG_REFLECT - Prefix for Angular reflection attributes
29
- * @property {string} RENDERED - Prefix for rendered component markers
30
- * @property {string} MAPPER - Key for property mappers
31
- * @property {string} CHILDREN - Key for child components
32
- * @property {string} LISTABLE - Key for listable components
33
- * @property {string} RENDER - Key for renderable components
34
- * @property {string} RENDERED_ID - Template for rendered component IDs
35
- * @property {string} PARENT - Key for comparison decorators and validators
36
- * @const AngularEngineKeys
37
- * @memberOf module:engine
38
- */
39
- const AngularEngineKeys = {
40
- REFLECT: `${UIKeys.REFLECT}.angular.`,
41
- DYNAMIC: 'dynamic-component',
42
- ANNOTATIONS: '__annotations__',
43
- ECMP: 'ecmp',
44
- NG_REFLECT: 'ng-reflect-',
45
- RENDERED: 'rendered-as-',
46
- MAPPER: 'mapper',
47
- CHILDREN: 'children',
48
- LISTABLE: 'listable',
49
- RENDER: 'render',
50
- RENDERED_ID: 'rendered-as-{0}',
51
- PARENT: '_parent',
52
- VALIDATION_PARENT_KEY: VALIDATION_PARENT_KEY
53
- };
54
- /**
55
- * @description Form validation state constants
56
- * @summary Contains constants representing the possible validation states of a form.
57
- * These are used to check and handle form validation throughout the application.
58
- * @typedef {Object} FormConstants
59
- * @property {string} VALID - Constant representing a valid form state
60
- * @property {string} INVALID - Constant representing an invalid form state
61
- * @const FormConstants
62
- * @memberOf module:engine
63
- */
64
- const FormConstants = {
65
- VALID: 'VALID',
66
- INVALID: 'INVALID',
67
- };
68
- /**
69
- * @description Event name constants
70
- * @summary Enum containing constants for event names used throughout the application.
71
- * These are used to standardize event naming and handling.
72
- * @enum {string}
73
- * @readonly
74
- * @property {string} BACK_BUTTON_NAVIGATION - Event fired when back button navigation ends
75
- * @property {string} REFRESH_EVENT - Event fired when a refresh action occurs
76
- * @property {string} CLICK_EVENT - Event fired when a click action occurs
77
- * @property {string} SUBMIT_EVENT - Event fired when a form submission occurs
78
- * @memberOf module:engine
79
- */
80
- var EventConstants;
81
- (function (EventConstants) {
82
- EventConstants["BACK_BUTTON_NAVIGATION"] = "backButtonNavigationEndEvent";
83
- EventConstants["REFRESH_EVENT"] = "RefreshEvent";
84
- EventConstants["CLICK_EVENT"] = "ClickEvent";
85
- EventConstants["SUBMIT_EVENT"] = "SubmitEvent";
86
- })(EventConstants || (EventConstants = {}));
87
- /**
88
- * @description Logger level constants
89
- * @summary Enum defining the logging levels used in the application's logging system.
90
- * Lower values represent more verbose logging, while higher values represent more critical logs.
91
- * @enum {number}
92
- * @readonly
93
- * @property {number} ALL - Log everything (most verbose)
94
- * @property {number} DEBUG - Log debug information
95
- * @property {number} INFO - Log informational messages
96
- * @property {number} WARN - Log warnings
97
- * @property {number} ERROR - Log errors
98
- * @property {number} CRITICAL - Log critical errors (least verbose)
99
- * @memberOf module:engine
100
- */
101
- var LoggerLevels;
102
- (function (LoggerLevels) {
103
- LoggerLevels[LoggerLevels["ALL"] = 0] = "ALL";
104
- LoggerLevels[LoggerLevels["DEBUG"] = 1] = "DEBUG";
105
- LoggerLevels[LoggerLevels["INFO"] = 2] = "INFO";
106
- LoggerLevels[LoggerLevels["WARN"] = 3] = "WARN";
107
- LoggerLevels[LoggerLevels["ERROR"] = 4] = "ERROR";
108
- LoggerLevels[LoggerLevels["CRITICAL"] = 5] = "CRITICAL";
109
- })(LoggerLevels || (LoggerLevels = {}));
110
- ;
111
- /**
112
- * @description Route direction constants
113
- * @summary Enum defining the possible navigation directions in the application.
114
- * Used for controlling navigation flow and animation directions.
115
- * @enum {string}
116
- * @readonly
117
- * @property {string} BACK - Navigate back to the previous page
118
- * @property {string} FORWARD - Navigate forward to the next page
119
- * @property {string} ROOT - Navigate to the root/home page
120
- * @memberOf module:engine
121
- */
122
- var RouteDirections;
123
- (function (RouteDirections) {
124
- RouteDirections["BACK"] = "back";
125
- RouteDirections["FORWARD"] = "forward";
126
- RouteDirections["ROOT"] = "root";
127
- })(RouteDirections || (RouteDirections = {}));
128
- /**
129
- * @description Component tag name constants
130
- * @summary Enum defining the tag names for custom components used in the application.
131
- * These tag names are used for component registration and rendering.
132
- * @enum {string}
133
- * @readonly
134
- * @property {string} LIST_ITEM - Tag name for list item component
135
- * @property {string} LIST_INFINITE - Tag name for infinite scrolling list component
136
- * @property {string} LIST_PAGINATED - Tag name for paginated list component
137
- * @memberOf module:engine
138
- */
139
- var ComponentsTagNames;
140
- (function (ComponentsTagNames) {
141
- ComponentsTagNames["LIST_ITEM"] = "ngx-decaf-list-item";
142
- ComponentsTagNames["LIST_INFINITE"] = "ngx-decaf-list-infinite";
143
- ComponentsTagNames["LIST_PAGINATED"] = "ngx-decaf-list-paginated";
144
- })(ComponentsTagNames || (ComponentsTagNames = {}));
145
- /**
146
- * @description Base component property name constants
147
- * @summary Enum defining the standard property names used by base components in the application.
148
- * These property names are used for consistent property access across components.
149
- * @enum {string}
150
- * @readonly
151
- * @property {string} MODEL - Property name for the component's data model
152
- * @property {string} LOCALE - Property name for localization settings
153
- * @property {string} PK - Property name for primary key
154
- * @property {string} ITEMS - Property name for collection items
155
- * @property {string} ROUTE - Property name for routing information
156
- * @property {string} OPERATIONS - Property name for available operations
157
- * @property {string} UID - Property name for unique identifier
158
- * @property {string} TRANSLATABLE - Property name for translation flag
159
- * @property {string} MAPPER - Property name for property mapper
160
- * @property {string} INITIALIZED - Property name for initialization state
161
- * @memberOf module:engine
162
- */
163
- var BaseComponentProps;
164
- (function (BaseComponentProps) {
165
- BaseComponentProps["MODEL"] = "model";
166
- BaseComponentProps["LOCALE"] = "locale";
167
- BaseComponentProps["PK"] = "pk";
168
- BaseComponentProps["ITEMS"] = "items";
169
- BaseComponentProps["ROUTE"] = "route";
170
- BaseComponentProps["OPERATIONS"] = "operations";
171
- BaseComponentProps["UID"] = "uid";
172
- BaseComponentProps["TRANSLATABLE"] = "translatable";
173
- BaseComponentProps["MAPPER"] = "mapper";
174
- BaseComponentProps["INITIALIZED"] = "initialized";
175
- })(BaseComponentProps || (BaseComponentProps = {}));
176
-
177
- /**
178
- *
179
- * Resolves the correct validator key and its associated properties based on the input key and type.
180
- *
181
- * When the validation key is TYPE, it's necessary to resolve the actual validator based on the
182
- * field's type (e.g., 'password', 'email', 'url') instead of using the generic getValidator("type") logic.
183
- * This allows directly invoking specific validators like getValidator('password'), ensuring the correct
184
- * behavior for type-based validation.
185
- *
186
- * @param key - The validation key (e.g., 'type', 'required', etc.).
187
- * @param value - The value that needs be provided to the validator.
188
- * @param type - The field's declared type.
189
- * @returns An object containing the resolved validator key and its corresponding props.
190
- */
191
- const resolveValidatorKeyProps = (key, value, type) => {
192
- const patternValidators = {
193
- [ValidationKeys.PASSWORD]: DEFAULT_PATTERNS.PASSWORD.CHAR8_ONE_OF_EACH,
194
- [ValidationKeys.EMAIL]: DEFAULT_PATTERNS.EMAIL,
195
- [ValidationKeys.URL]: DEFAULT_PATTERNS.URL,
196
- };
197
- const isTypeBased = key === ValidationKeys.TYPE && Object.keys(patternValidators).includes(type);
198
- const validatorKey = isTypeBased ? type : key;
199
- const props = {
200
- [validatorKey]: value,
201
- // Email, Password, and URL are validated using the "pattern" key
202
- ...(isTypeBased && { [ValidationKeys.PATTERN]: patternValidators[type] }),
203
- };
204
- return { validatorKey, props };
205
- };
206
- class ValidatorFactory {
207
- static spawn(fieldProps, key) {
208
- if (!Validation.keys().includes(key))
209
- throw new Error('Unsupported custom validation');
210
- const validatorFn = (control) => {
211
- const { name, type } = fieldProps;
212
- const { validatorKey, props } = resolveValidatorKeyProps(key, fieldProps[key], type);
213
- const validator = Validation.get(validatorKey);
214
- // parseValueByType does not support undefined values
215
- const value = typeof control.value !== 'undefined'
216
- ? parseValueByType(type, type === HTML5InputTypes.CHECKBOX ? name : control.value, fieldProps)
217
- : undefined;
218
- // Create a proxy to enable access to parent and child values
219
- let proxy = ValidatorFactory.createProxy({});
220
- if (Object.values(ComparisonValidationKeys).includes(key)) {
221
- const parent = control instanceof FormGroup ? control : control[AngularEngineKeys.PARENT];
222
- proxy = ValidatorFactory.createProxy(parent);
223
- }
224
- let errs;
225
- try {
226
- errs = validator.hasErrors(value, props, proxy);
227
- }
228
- catch (e) {
229
- errs = `${key} validator failed to validate: ${e}`;
230
- console.warn(errs);
231
- }
232
- return errs ? { [validatorKey]: true } : null;
233
- };
234
- Object.defineProperty(validatorFn, 'name', {
235
- value: `${key}Validator`,
236
- });
237
- return validatorFn;
238
- }
239
- /**
240
- * @summary Creates a proxy wrapper for an Angular AbstractControl to assist with custom validation logic.
241
- * @description Returns a structured proxy object that simulates a hierarchical tree of form values.
242
- * Enables Validators handling method to access parent and child properties using consistent dot-notation in Angular forms.
243
- *
244
- * @param {AbstractControl} control - The control to wrap in a proxy.
245
- * @returns {PathProxy<unknown>} A proxy object exposing form values and enabling recursive parent access.
246
- */
247
- static createProxy(control) {
248
- return PathProxyEngine.create(control, {
249
- getValue(target, prop) {
250
- if (target instanceof FormControl)
251
- return target.value;
252
- if (target instanceof FormGroup) {
253
- const control = target.controls[prop];
254
- return control instanceof FormControl ? control.value : control;
255
- }
256
- // const value = target[prop];
257
- // if (value instanceof FormControl)
258
- // return value.value;
259
- //
260
- // if (value instanceof FormGroup) {
261
- // const control = value.controls[prop];
262
- // return control instanceof FormControl ? control.value : control;
263
- // }
264
- return target?.[prop];
265
- },
266
- getParent: function (target) {
267
- return target?.['_parent'];
268
- },
269
- ignoreUndefined: true,
270
- ignoreNull: true,
271
- });
272
- }
273
- }
274
-
275
- /**
276
- * @description Service for managing Angular forms and form controls.
277
- * @summary The NgxFormService provides utility methods for creating, managing, and validating Angular forms and form controls. It includes functionality for registering forms, adding controls, validating fields, and handling form data.
278
- *
279
- * @class
280
- * @param {WeakMap<AbstractControl, FieldProperties>} controls - A WeakMap to store control properties.
281
- * @param {Map<string, FormGroup>} formRegistry - A Map to store registered forms.
282
- *
283
- * @example
284
- * // Creating a form from components
285
- * const components = [
286
- * { inputs: { name: 'username', type: 'text', required: true } },
287
- * { inputs: { name: 'password', type: 'password', minLength: 8 } }
288
- * ];
289
- * const form = NgxFormService.createFormFromComponents('loginForm', components, true);
290
- *
291
- * // Validating fields
292
- * NgxFormService.validateFields(form);
293
- *
294
- * // Getting form data
295
- * const formData = NgxFormService.getFormData(form);
296
- *
297
- * @mermaid
298
- * sequenceDiagram
299
- * participant C as Component
300
- * participant NFS as NgxFormService
301
- * participant AF as Angular Forms
302
- * C->>NFS: createFormFromComponents()
303
- * NFS->>AF: new FormGroup()
304
- * NFS->>NFS: addFormControl()
305
- * NFS->>AF: addControl()
306
- * NFS-->>C: Return FormGroup
307
- * C->>NFS: validateFields()
308
- * NFS->>AF: markAsTouched(), markAsDirty(), updateValueAndValidity()
309
- * C->>NFS: getFormData()
310
- * NFS->>AF: Get control values
311
- * NFS-->>C: Return form data
312
- */
313
- class NgxFormService {
314
- static { this.controls = new WeakMap(); }
315
- static { this.formRegistry = new Map(); }
316
- /**
317
- * @description Adds a form to the registry.
318
- * @summary Registers a FormGroup with a unique identifier. Throws an error if the identifier is already in use.
319
- * @param {string} formId - The unique identifier for the form.
320
- * @param {FormGroup} formGroup - The FormGroup to be registered.
321
- * @throws {Error} If a FormGroup with the given id is already registered.
322
- */
323
- static addRegistry(formId, formGroup) {
324
- if (this.formRegistry.has(formId))
325
- throw new Error(`A FormGroup with id '${formId}' is already registered.`);
326
- this.formRegistry.set(formId, formGroup);
327
- }
328
- /**
329
- * @description Removes a form from the registry.
330
- * @summary Deletes a FormGroup from the registry using its unique identifier.
331
- * @param {string} formId - The unique identifier of the form to be removed.
332
- */
333
- static removeRegistry(formId) {
334
- this.formRegistry.delete(formId);
335
- }
336
- /**
337
- * @description Resolves the parent group and control name from a path.
338
- * @summary Traverses the form group structure to find the parent group and control name for a given path.
339
- * @param {FormGroup} formGroup - The root FormGroup.
340
- * @param {string} path - The path to the control.
341
- * @return {FormParentGroup} A tuple containing the parent FormGroup and the control name.
342
- */
343
- static resolveParentGroup(formGroup, path) {
344
- const parts = path.split('.');
345
- const controlName = parts.pop();
346
- let currentGroup = formGroup;
347
- for (const part of parts) {
348
- if (!currentGroup.get(part)) {
349
- currentGroup.addControl(part, new FormGroup({}));
350
- }
351
- currentGroup = currentGroup.get(part);
352
- }
353
- return [currentGroup, controlName];
354
- }
355
- /**
356
- * @description Adds a form control to a form group.
357
- * @summary Creates and adds a form control to the specified form group based on the provided component properties.
358
- * @param {FormGroup} formGroup - The form group to add the control to.
359
- * @param {ComponentInput} componentProps - The properties of the component to create the control from.
360
- */
361
- static addFormControl(formGroup, componentProps) {
362
- const { name, childOf } = componentProps;
363
- const fullPath = childOf ? `${childOf}.${name}` : name;
364
- const [parentGroup, controlName] = this.resolveParentGroup(formGroup, fullPath);
365
- if (!parentGroup.get(controlName)) {
366
- const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
367
- NgxFormService.register(control, componentProps);
368
- parentGroup.addControl(controlName, control);
369
- }
370
- componentProps['formGroup'] = parentGroup;
371
- componentProps['formControl'] = parentGroup.get(controlName);
372
- }
373
- /**
374
- * @description Retrieves a control from a registered form.
375
- * @summary Finds and returns an AbstractControl from a registered form using the form id and optional path.
376
- * @param {string} formId - The unique identifier of the form.
377
- * @param {string} [path] - The path to the control within the form.
378
- * @return {AbstractControl} The requested AbstractControl.
379
- * @throws {Error} If the form is not found in the registry or the control is not found in the form.
380
- */
381
- static getControlFromForm(formId, path) {
382
- const form = this.formRegistry.get(formId);
383
- if (!form)
384
- throw new Error(`Form with id '${formId}' not found in the registry.`);
385
- if (!path)
386
- return form;
387
- const control = form.get(path);
388
- if (!control)
389
- throw new Error(`Control with path '${path}' not found in form '${formId}'.`);
390
- return control;
391
- }
392
- /**
393
- * @description Creates a form from component configurations.
394
- * @summary Generates a FormGroup based on an array of component configurations and optionally registers it.
395
- * @param {string} id - The unique identifier for the form.
396
- * @param {ComponentConfig[]} components - An array of component configurations.
397
- * @param {boolean} [registry=false] - Whether to register the created form.
398
- * @return {FormGroup} The created FormGroup.
399
- */
400
- static createFormFromComponents(id, components, registry = false) {
401
- const form = new FormGroup({});
402
- components.forEach(component => {
403
- this.addFormControl(form, component.inputs);
404
- });
405
- if (registry)
406
- this.addRegistry(id, form);
407
- return form;
408
- }
409
- /**
410
- * @description Adds a control to a form based on component properties.
411
- * @summary Creates and adds a form control to a form (existing or new) based on the provided component properties.
412
- * @param {string} id - The unique identifier of the form.
413
- * @param {FieldProperties} componentProperties - The properties of the component to create the control from.
414
- * @return {AbstractControl} The form or created control.
415
- */
416
- static addControlFromProps(id, componentProperties) {
417
- const form = this.formRegistry.get(id) ?? new FormGroup({});
418
- if (!this.formRegistry.has(id))
419
- this.addRegistry(id, form);
420
- if (componentProperties.path)
421
- this.addFormControl(form, componentProperties);
422
- return form;
423
- }
424
- /**
425
- * @description Retrieves form data from a FormGroup.
426
- * @summary Extracts and processes the data from a FormGroup, handling different input types and nested form groups.
427
- * @param {FormGroup} formGroup - The FormGroup to extract data from.
428
- * @return {Record<string, unknown>} An object containing the form data.
429
- */
430
- static getFormData(formGroup) {
431
- const data = {};
432
- for (const key in formGroup.controls) {
433
- const control = formGroup.controls[key];
434
- if (!(control instanceof FormControl)) {
435
- data[key] = NgxFormService.getFormData(control);
436
- continue;
437
- }
438
- const props = NgxFormService.getPropsFromControl(control);
439
- let value = control.value;
440
- if (!HTML5CheckTypes.includes(props['type'])) {
441
- switch (props['type']) {
442
- case HTML5InputTypes.NUMBER:
443
- value = parseToNumber(value);
444
- break;
445
- case HTML5InputTypes.DATE:
446
- case HTML5InputTypes.DATETIME_LOCAL:
447
- value = new Date(value);
448
- break;
449
- default:
450
- value = escapeHtml(value);
451
- }
452
- }
453
- data[key] = value;
454
- }
455
- return data;
456
- }
457
- /**
458
- * @description Validates fields in a form control or form group.
459
- * @summary Recursively validates all fields in a form control or form group, marking them as touched and dirty.
460
- * @param {AbstractControl} control - The control or form group to validate.
461
- * @param {string} [path] - The path to the control within the form.
462
- * @return {boolean} True if all fields are valid, false otherwise.
463
- * @throws {Error} If no control is found at the specified path or if the control type is unknown.
464
- */
465
- static validateFields(control, path) {
466
- control = path ? control.get(path) : control;
467
- if (!control)
468
- throw new Error(`No control found at path: ${path || 'root'}.`);
469
- const isAllowed = [FormGroup, FormControl].some(type => control instanceof type);
470
- if (!isAllowed)
471
- throw new Error(`Unknown control type at: ${path || 'root'}`);
472
- control.markAsTouched();
473
- control.markAsDirty();
474
- control.updateValueAndValidity({ emitEvent: true });
475
- if (control instanceof FormGroup) {
476
- Object.values(control.controls).forEach((childControl) => {
477
- this.validateFields(childControl);
478
- });
479
- }
480
- return control.valid;
481
- }
482
- /**
483
- * @description Generates validators from component properties.
484
- * @summary Creates an array of ValidatorFn based on the supported validation keys in the component properties.
485
- * @param {FieldProperties} props - The component properties.
486
- * @return {ValidatorFn[]} An array of validator functions.
487
- */
488
- static validatorsFromProps(props) {
489
- const supportedValidationKeys = Validation.keys();
490
- return Object.keys(props)
491
- .filter((k) => supportedValidationKeys.includes(k))
492
- .map((k) => {
493
- return ValidatorFactory.spawn(props, k);
494
- });
495
- }
496
- /**
497
- * @description Creates a FormControl from component properties.
498
- * @summary Generates a FormControl with validators based on the provided component properties.
499
- * @param {FieldProperties} props - The component properties.
500
- * @param {FieldUpdateMode} [updateMode='change'] - The update mode for the control.
501
- * @return {FormControl} The created FormControl.
502
- */
503
- static fromProps(props, updateMode = 'change') {
504
- const validators = this.validatorsFromProps(props);
505
- const composed = validators.length ? Validators.compose(validators) : null;
506
- return new FormControl({
507
- value: props.value && props.type !== HTML5InputTypes.CHECKBOX
508
- ? props.type === HTML5InputTypes.DATE
509
- ? !isValidDate(parseDate(props.format, props.value))
510
- ? undefined : props.value :
511
- props.value : undefined,
512
- disabled: props.disabled,
513
- }, {
514
- validators: composed,
515
- updateOn: updateMode,
516
- });
517
- }
518
- /**
519
- * @description Retrieves properties from a FormControl.
520
- * @summary Gets the FieldProperties associated with a FormControl from the internal WeakMap.
521
- * @param {FormControl} control - The FormControl to get properties for.
522
- * @return {FieldProperties} The properties associated with the control.
523
- */
524
- static getPropsFromControl(control) {
525
- return this.controls.get(control) || {};
526
- }
527
- /**
528
- * @description Finds a parent element with a specific tag.
529
- * @summary Traverses up the DOM tree to find the nearest parent element with the specified tag.
530
- * @param {HTMLElement} el - The starting element.
531
- * @param {string} tag - The tag name to search for.
532
- * @return {HTMLElement} The found parent element.
533
- * @throws {Error} If no parent with the specified tag is found.
534
- */
535
- static getParentEl(el, tag) {
536
- let parent;
537
- while ((parent = el.parentElement) !== null) {
538
- if (parent.tagName.toLowerCase() === tag.toLowerCase()) {
539
- return parent;
540
- }
541
- el = parent;
542
- }
543
- throw new Error(`No parent with the tag ${tag} was found for provided element`);
544
- }
545
- /**
546
- * @description Registers a control with its properties.
547
- * @summary Associates a control with its properties in the internal WeakMap.
548
- * @param {AbstractControl} control - The control to register.
549
- * @param {FieldProperties} props - The properties to associate with the control.
550
- */
551
- static register(control, props) {
552
- this.controls.set(control, props);
553
- }
554
- /**
555
- * @description Unregisters a control.
556
- * @summary Removes a control and its associated properties from the internal WeakMap.
557
- * @param {AbstractControl} control - The control to unregister.
558
- * @return {boolean} True if the control was successfully unregistered, false otherwise.
559
- */
560
- static unregister(control) {
561
- return this.controls.delete(control);
562
- }
563
- /**
564
- * @description Resets a form group.
565
- * @summary Recursively resets all controls in a form group, clearing values, errors, and marking them as pristine and untouched.
566
- * @param {FormGroup} formGroup - The form group to reset.
567
- */
568
- static reset(formGroup) {
569
- for (const key in formGroup.controls) {
570
- const control = formGroup.controls[key];
571
- if (!(control instanceof FormControl)) {
572
- NgxFormService.reset(control);
573
- continue;
574
- }
575
- const { type } = NgxFormService.getPropsFromControl(control);
576
- if (!HTML5CheckTypes.includes(type))
577
- control.setValue(undefined);
578
- control.markAsPristine();
579
- control.markAsUntouched();
580
- control.setErrors(null);
581
- control.updateValueAndValidity();
582
- }
583
- }
584
- }
585
-
586
- /**
587
- * @description Angular implementation of the RenderingEngine with enhanced features
588
- * @summary This class extends the base RenderingEngine to provide Angular-specific rendering capabilities
589
- * with additional features compared to NgxRenderingEngine. It handles the conversion of field definitions
590
- * to Angular components, manages component registration, and provides utilities for component creation
591
- * and input handling. This implementation uses Angular's newer component APIs.
592
- *
593
- * @template AngularFieldDefinition - Type for Angular-specific field definitions
594
- * @template AngularDynamicOutput - Type for Angular-specific component output
595
- *
596
- * @class NgxRenderingEngine2
597
- * @example
598
- * ```typescript
599
- * const engine = NgxRenderingEngine2.get();
600
- * engine.initialize();
601
- * const output = engine.render(myModel, {}, viewContainerRef, injector, templateRef);
602
- * ```
603
- *
604
- * @mermaid
605
- * sequenceDiagram
606
- * participant Client
607
- * participant Engine as NgxRenderingEngine2
608
- * participant Components as RegisteredComponents
609
- *
610
- * Client->>Engine: get()
611
- * Client->>Engine: initialize()
612
- * Client->>Engine: render(model, props, vcr, injector, tpl)
613
- * Engine->>Engine: toFieldDefinition(model, props)
614
- * Engine->>Engine: fromFieldDefinition(fieldDef, vcr, injector, tpl)
615
- * Engine->>Components: components(fieldDef.tag)
616
- * Components-->>Engine: component constructor
617
- * Engine->>Engine: createComponent(component, inputs, metadata, vcr, injector, template)
618
- * Engine-->>Client: return AngularDynamicOutput
619
- */
620
- class NgxRenderingEngine2 extends RenderingEngine {
621
- /**
622
- * @description Creates a new instance of NgxRenderingEngine2
623
- * @summary Initializes the rendering engine with the 'angular' engine type.
624
- * This constructor sets up the base configuration needed for Angular-specific rendering.
625
- */
626
- constructor() {
627
- super('angular');
628
- }
629
- /**
630
- * @description Converts a field definition to an Angular component output
631
- * @summary This private method takes a field definition and creates the corresponding Angular component.
632
- * It handles component instantiation, input property mapping, and child component rendering.
633
- * The method validates input properties against the component's metadata and processes
634
- * child components recursively.
635
- *
636
- * @param {FieldDefinition<AngularFieldDefinition>} fieldDef - The field definition to convert
637
- * @param {ViewContainerRef} vcr - The view container reference for component creation
638
- * @param {Injector} injector - The Angular injector for dependency injection
639
- * @param {TemplateRef<any>} tpl - The template reference for content projection
640
- * @param {string} registryFormId - Form identifier for the component renderer
641
- * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
642
- *
643
- * @mermaid
644
- * sequenceDiagram
645
- * participant Method as fromFieldDefinition
646
- * participant Components as NgxRenderingEngine2.components
647
- * participant Angular as Angular Core
648
- * participant Process as processChild
649
- *
650
- * Method->>Components: components(fieldDef.tag)
651
- * Components-->>Method: component constructor
652
- * Method->>Angular: reflectComponentType(component)
653
- * Angular-->>Method: componentMetadata
654
- * Method->>Method: Validate input properties
655
- * Method->>Method: Create result object
656
- * alt Has children
657
- * Method->>Process: Process children recursively
658
- * Process->>Method: Return processed children
659
- * Method->>Angular: Create embedded view
660
- * Method->>Method: Create component instance
661
- * end
662
- * Method-->>Caller: return AngularDynamicOutput
663
- */
664
- fromFieldDefinition(fieldDef, vcr, injector, tpl, registryFormId = Date.now().toString(36).toUpperCase()) {
665
- const cmp = fieldDef?.['component'] || NgxRenderingEngine2.components(fieldDef.tag);
666
- const component = cmp.constructor;
667
- const componentMetadata = reflectComponentType(component);
668
- if (!componentMetadata) {
669
- throw new InternalError(`Metadata for component ${fieldDef.tag} not found.`);
670
- }
671
- const { inputs: possibleInputs } = componentMetadata;
672
- const inputs = { ...fieldDef.props };
673
- const unmappedKeys = Object.keys(inputs).filter(input => {
674
- const isMapped = possibleInputs.find(({ propName }) => propName === input);
675
- if (!isMapped)
676
- delete inputs[input];
677
- return !isMapped;
678
- });
679
- if (unmappedKeys.length > 0)
680
- console.warn(`Unmapped input properties for component ${fieldDef.tag}: ${unmappedKeys.join(', ')}`);
681
- const result = {
682
- component,
683
- inputs,
684
- injector,
685
- };
686
- if (fieldDef.rendererId)
687
- result.inputs['rendererId'] = fieldDef.rendererId;
688
- // process children
689
- if (fieldDef.children?.length) {
690
- result.children = fieldDef.children.map((child) => {
691
- // create a child form and add its controls as properties of child.props
692
- NgxFormService.addControlFromProps(registryFormId, child.props);
693
- return this.fromFieldDefinition(child, vcr, injector, tpl, registryFormId);
694
- });
695
- }
696
- // generating DOM
697
- vcr.clear();
698
- const template = vcr.createEmbeddedView(tpl, injector).rootNodes;
699
- const componentInstance = NgxRenderingEngine2.createComponent(component, { ...inputs, model: this._model }, componentMetadata, vcr, injector, template);
700
- result.instance = NgxRenderingEngine2._instance = componentInstance.instance;
701
- return result;
702
- }
703
- /**
704
- * @description Creates an Angular component instance
705
- * @summary This static utility method creates an Angular component instance with the specified
706
- * inputs and template. It uses Angular's component creation API to instantiate the component
707
- * and then sets the input properties using the provided metadata.
708
- *
709
- * @param {Type<unknown>} component - The component type to create
710
- * @param {KeyValue} [inputs={}] - The input properties to set on the component
711
- * @param {ComponentMirror<unknown>} metadata - The component metadata for input validation
712
- * @param {ViewContainerRef} vcr - The view container reference for component creation
713
- * @param {Injector} injector - The Angular injector for dependency injection
714
- * @param {Node[]} [template=[]] - The template nodes to project into the component
715
- * @return {ComponentRef<unknown>} The created component reference
716
- */
717
- static createComponent(component, inputs = {}, metadata, vcr, injector, template = []) {
718
- const componentInstance = vcr.createComponent(component, {
719
- environmentInjector: injector,
720
- projectableNodes: [template],
721
- });
722
- this.setInputs(componentInstance, inputs, metadata);
723
- return componentInstance;
724
- }
725
- /**
726
- * @description Extracts decorator metadata from a model
727
- * @summary This method provides access to the field definition generated from a model's
728
- * decorators. It's a convenience wrapper around the toFieldDefinition method that
729
- * converts a model to a field definition based on its decorators and the provided
730
- * global properties.
731
- *
732
- * @param {Model} model - The model to extract decorators from
733
- * @param {Record<string, unknown>} globalProps - Global properties to include in the field definition
734
- * @return {FieldDefinition<AngularFieldDefinition>} The field definition generated from the model
735
- */
736
- getDecorators(model, globalProps) {
737
- return this.toFieldDefinition(model, globalProps);
738
- }
739
- /**
740
- * @description Destroys the current engine instance
741
- * @summary This static method clears the current instance reference, effectively
742
- * destroying the singleton instance of the rendering engine. This can be used
743
- * to reset the engine state or to prepare for a new instance creation.
744
- *
745
- * @return {Promise<void>} A promise that resolves when the instance is destroyed
746
- */
747
- static async destroy() {
748
- NgxRenderingEngine2._instance = undefined;
749
- }
750
- /**
751
- * @description Renders a model into an Angular component output
752
- * @summary This method takes a model and converts it to an Angular component output.
753
- * It first stores a reference to the model, then converts it to a field definition
754
- * using the base RenderingEngine's toFieldDefinition method, and finally converts
755
- * that field definition to an Angular component output using fromFieldDefinition.
756
- *
757
- * @template M - Type extending Model
758
- * @param {M} model - The model to render
759
- * @param {Record<string, unknown>} globalProps - Global properties to pass to the component
760
- * @param {ViewContainerRef} vcr - The view container reference for component creation
761
- * @param {Injector} injector - The Angular injector for dependency injection
762
- * @param {TemplateRef<any>} tpl - The template reference for content projection
763
- * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
764
- *
765
- * @mermaid
766
- * sequenceDiagram
767
- * participant Client as Client Code
768
- * participant Render as render method
769
- * participant ToField as toFieldDefinition
770
- * participant FromField as fromFieldDefinition
771
- *
772
- * Client->>Render: render(model, globalProps, vcr, injector, tpl)
773
- * Render->>Render: Store model reference
774
- * Render->>ToField: toFieldDefinition(model, globalProps)
775
- * ToField-->>Render: fieldDef
776
- * Render->>FromField: fromFieldDefinition(fieldDef, vcr, injector, tpl)
777
- * FromField-->>Render: AngularDynamicOutput
778
- * Render-->>Client: return AngularDynamicOutput
779
- */
780
- render(model, globalProps, vcr, injector, tpl) {
781
- let result;
782
- try {
783
- this._model = model;
784
- const formId = Date.now().toString(36).toUpperCase();
785
- const fieldDef = this.toFieldDefinition(model, globalProps);
786
- result = this.fromFieldDefinition(fieldDef, vcr, injector, tpl, formId);
787
- result.instance['formGroup'] = NgxFormService.getControlFromForm(formId);
788
- NgxFormService.removeRegistry(formId);
789
- }
790
- catch (e) {
791
- throw new InternalError(`Failed to render Model ${model.constructor.name}: ${e}`);
792
- }
793
- return result;
794
- }
795
- /**
796
- * @description Initializes the rendering engine
797
- * @summary This method initializes the rendering engine. It checks if the engine is already initialized
798
- * and sets the initialized flag to true. This method is called before the engine is used
799
- * to ensure it's properly set up for rendering operations.
800
- *
801
- * @return {Promise<void>} A promise that resolves when initialization is complete
802
- */
803
- async initialize() {
804
- if (this.initialized)
805
- return;
806
- // ValidatableByType[]
807
- this.initialized = true;
808
- }
809
- /**
810
- * @description Registers a component with the rendering engine
811
- * @summary This static method registers a component constructor with the rendering engine
812
- * under a specific name. It initializes the components registry if needed and throws
813
- * an error if a component is already registered under the same name to prevent
814
- * accidental overrides.
815
- *
816
- * @param {string} name - The name to register the component under
817
- * @param {Constructor<unknown>} constructor - The component constructor
818
- * @return {void}
819
- */
820
- static registerComponent(name, constructor) {
821
- if (!this._components)
822
- this._components = {};
823
- if (name in this._components)
824
- throw new InternalError(`Component already registered under ${name}`);
825
- this._components[name] = {
826
- constructor: constructor,
827
- };
828
- }
829
- /**
830
- * @description Retrieves registered components from the rendering engine
831
- * @summary This static method retrieves either all registered components or a specific component
832
- * by its selector. When called without a selector, it returns an array of all registered
833
- * components. When called with a selector, it returns the specific component if found,
834
- * or throws an error if the component is not registered.
835
- *
836
- * @param {string} [selector] - Optional selector to retrieve a specific component
837
- * @return {Object|Array} Either a specific component or an array of all components
838
- */
839
- static components(selector) {
840
- if (!selector)
841
- return Object.values(this._components);
842
- if (!(selector in this._components))
843
- throw new InternalError(`No Component registered under ${selector}`);
844
- return this._components[selector];
845
- }
846
- /**
847
- * @description Generates a key for reflection metadata
848
- * @summary This static method generates a key for reflection metadata by prefixing the input key
849
- * with the Angular engine's reflection prefix. This is used for storing and retrieving
850
- * metadata in a namespaced way to avoid conflicts with other metadata.
851
- *
852
- * @param {string} key - The base key to prefix
853
- * @return {string} The prefixed key for reflection metadata
854
- */
855
- static key(key) {
856
- return `${AngularEngineKeys.REFLECT}${key}`;
857
- }
858
- /**
859
- * @description Sets input properties on a component instance
860
- * @summary This static utility method sets input properties on a component instance
861
- * based on the provided inputs object and component metadata. It handles both simple
862
- * values and nested objects, recursively processing object properties. The method
863
- * validates each input against the component's metadata to ensure only valid inputs
864
- * are set.
865
- *
866
- * @param {ComponentRef<unknown>} component - The component reference to set inputs on
867
- * @param {KeyValue} inputs - The input properties to set
868
- * @param {ComponentMirror<unknown>} metadata - The component metadata for input validation
869
- * @return {void}
870
- *
871
- * @mermaid
872
- * sequenceDiagram
873
- * participant Caller
874
- * participant SetInputs as setInputs
875
- * participant Parse as parseInputValue
876
- * participant Component as ComponentRef
877
- *
878
- * Caller->>SetInputs: setInputs(component, inputs, metadata)
879
- * SetInputs->>SetInputs: Iterate through inputs
880
- * loop For each input
881
- * SetInputs->>SetInputs: Check if input exists in metadata
882
- * alt Input is 'props'
883
- * SetInputs->>Parse: parseInputValue(component, value)
884
- * Parse->>Parse: Recursively process nested objects
885
- * Parse->>Component: setInput(key, value)
886
- * else Input is valid
887
- * SetInputs->>Component: setInput(key, value)
888
- * end
889
- * end
890
- */
891
- static setInputs(component, inputs, metadata) {
892
- function parseInputValue(component, input) {
893
- Object.keys(input).forEach(key => {
894
- const value = input[key];
895
- if (typeof value === 'object' && !!value)
896
- return parseInputValue(component, value);
897
- component.setInput(key, value);
898
- });
899
- }
900
- Object.entries(inputs).forEach(([key, value]) => {
901
- const prop = metadata.inputs.find((item) => item.propName === key);
902
- if (prop) {
903
- if (key === 'props')
904
- parseInputValue(component, value);
905
- // if(key === 'locale' && !value)
906
- // value = getLocaleFromClassName(this._componentName);
907
- component.setInput(key, value);
908
- }
909
- });
910
- }
911
- }
912
-
913
- /**
914
- * @description Marks an Angular component as dynamically loadable
915
- * @summary Decorator that registers an Angular component with the NgxRenderingEngine2 for dynamic loading.
916
- * This decorator must be applied before the @Component decorator to properly extract component metadata.
917
- * It adds metadata to the component class and registers it with the rendering engine using its selector.
918
- * @function Dynamic
919
- * @return {Function} A decorator function that can be applied to Angular component classes
920
- * @mermaid
921
- * sequenceDiagram
922
- * participant C as Component Class
923
- * participant D as Dynamic Decorator
924
- * participant R as NgxRenderingEngine2
925
- * participant M as Angular Metadata
926
- * C->>D: Apply decorator
927
- * D->>M: reflectComponentType()
928
- * M-->>D: Return component metadata
929
- * alt No metadata found
930
- * D->>D: Throw InternalError
931
- * else Metadata found
932
- * D->>R: registerComponent(selector, constructor)
933
- * D->>C: Apply metadata
934
- * end
935
- * @category Decorators
936
- */
937
- function Dynamic() {
938
- return apply((original) => {
939
- const metadata = reflectComponentType(original);
940
- if (!metadata)
941
- throw new InternalError(`Could not find Component metadata. @Dynamic decorator must come above @Component`);
942
- NgxRenderingEngine2.registerComponent(metadata.selector, original);
943
- }, metadata(NgxRenderingEngine2.key(AngularEngineKeys.DYNAMIC), true));
944
- }
945
-
946
- /**
947
- * @description Abstract base class for dynamic Angular modules
948
- * @summary The DynamicModule serves as a base class for Angular modules that need to be
949
- * dynamically loaded or configured at runtime. It provides a common type for the rendering
950
- * engine to identify and work with dynamic modules.
951
- * @class DynamicModule
952
- * @example
953
- * ```typescript
954
- * @NgModule({
955
- * declarations: [MyComponent],
956
- * imports: [CommonModule]
957
- * })
958
- * export class MyDynamicModule extends DynamicModule {}
959
- * ```
960
- */
961
- class DynamicModule {
962
- }
963
-
964
- /**
965
- * @description Angular implementation of the RenderingEngine
966
- * @summary This class extends the base RenderingEngine to provide Angular-specific rendering capabilities.
967
- * It handles the conversion of field definitions to Angular components and manages component registration.
968
- * @template AngularFieldDefinition - Type for Angular-specific field definitions
969
- * @template AngularDynamicOutput - Type for Angular-specific component output
970
- * @param {Injector} injector - Angular injector for dependency injection
971
- * @param {ViewContainerRef} vcr - View container reference for component creation
972
- * @param {TemplateRef<any>} tpl - Template reference for content projection
973
- * @class NgxRenderingEngine
974
- * @example
975
- * ```typescript
976
- * const engine = new NgxRenderingEngine();
977
- * engine.initialize();
978
- * const output = engine.render(myModel, {}, viewContainerRef, injector, templateRef);
979
- * ```
980
- * @mermaid
981
- * sequenceDiagram
982
- * participant Client
983
- * participant Engine as NgxRenderingEngine
984
- * participant Components as RegisteredComponents
985
- *
986
- * Client->>Engine: new NgxRenderingEngine()
987
- * Client->>Engine: initialize()
988
- * Client->>Engine: render(model, props, vcr, injector, tpl)
989
- * Engine->>Engine: toFieldDefinition(model, props)
990
- * Engine->>Engine: fromFieldDefinition(fieldDef, vcr, injector, tpl)
991
- * Engine->>Components: components(fieldDef.tag)
992
- * Components-->>Engine: component constructor
993
- * Engine->>Client: return AngularDynamicOutput
994
- */
995
- class NgxRenderingEngine extends RenderingEngine {
996
- constructor() {
997
- super('angular');
998
- }
999
- /**
1000
- * @description Converts a field definition to an Angular component output
1001
- * @summary This private method takes a field definition and creates the corresponding Angular component.
1002
- * It handles component instantiation, input property mapping, and child component rendering.
1003
- * @param {FieldDefinition<AngularFieldDefinition>} fieldDef - The field definition to convert
1004
- * @param {ViewContainerRef} vcr - The view container reference for component creation
1005
- * @param {Injector} injector - The Angular injector for dependency injection
1006
- * @param {TemplateRef<any>} tpl - The template reference for content projection
1007
- * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
1008
- * @mermaid
1009
- * sequenceDiagram
1010
- * participant Method as fromFieldDefinition
1011
- * participant Components as NgxRenderingEngine.components
1012
- * participant Angular as Angular Core
1013
- *
1014
- * Method->>Components: components(fieldDef.tag)
1015
- * Components-->>Method: component constructor
1016
- * Method->>Angular: reflectComponentType(component)
1017
- * Angular-->>Method: componentMetadata
1018
- * Method->>Method: Check input properties
1019
- * Method->>Method: Create result object
1020
- * Method->>Method: Process children if any
1021
- * Method-->>Caller: return AngularDynamicOutput
1022
- */
1023
- fromFieldDefinition(fieldDef, vcr, injector, tpl) {
1024
- const component = NgxRenderingEngine.components(fieldDef.tag)
1025
- .constructor;
1026
- const componentMetadata = reflectComponentType(component);
1027
- if (!componentMetadata) {
1028
- throw new InternalError(`Metadata for component ${fieldDef.tag} not found.`);
1029
- }
1030
- const inputs = fieldDef.props;
1031
- const possibleInputs = componentMetadata.inputs;
1032
- const inputKeys = Object.keys(inputs);
1033
- for (const input of possibleInputs) {
1034
- const index = inputKeys.indexOf(input.propName);
1035
- if (index !== -1) {
1036
- inputKeys.splice(index, 1);
1037
- }
1038
- if (!inputKeys.length)
1039
- break;
1040
- }
1041
- if (inputKeys.length)
1042
- console.warn(`Unmapped input properties for component ${fieldDef.tag}: ${inputKeys.join(', ')}`);
1043
- const result = {
1044
- component: component,
1045
- inputs: inputs || {},
1046
- injector: injector,
1047
- };
1048
- if (fieldDef.rendererId) {
1049
- result.inputs['rendererId'] =
1050
- fieldDef.rendererId;
1051
- }
1052
- if (fieldDef.children && fieldDef.children.length) {
1053
- result.children = fieldDef.children.map((child) => {
1054
- return this.fromFieldDefinition(child, vcr, injector, tpl);
1055
- });
1056
- const template = vcr.createEmbeddedView(tpl, injector).rootNodes;
1057
- result.content = [template];
1058
- }
1059
- return result;
1060
- }
1061
- /**
1062
- * @description Renders a model into an Angular component output
1063
- * @summary This method takes a model and converts it to an Angular component output.
1064
- * It first converts the model to a field definition using the base RenderingEngine's
1065
- * toFieldDefinition method, then converts that field definition to an Angular component output.
1066
- * @template M - Type extending Model
1067
- * @param {M} model - The model to render
1068
- * @param {Record<string, unknown>} globalProps - Global properties to pass to the component
1069
- * @param {ViewContainerRef} vcr - The view container reference for component creation
1070
- * @param {Injector} injector - The Angular injector for dependency injection
1071
- * @param {TemplateRef<any>} tpl - The template reference for content projection
1072
- * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
1073
- * @mermaid
1074
- * sequenceDiagram
1075
- * participant Client as Client Code
1076
- * participant Render as render method
1077
- * participant ToField as toFieldDefinition
1078
- * participant FromField as fromFieldDefinition
1079
- *
1080
- * Client->>Render: render(model, globalProps, vcr, injector, tpl)
1081
- * Render->>ToField: toFieldDefinition(model, globalProps)
1082
- * ToField-->>Render: fieldDef
1083
- * Render->>FromField: fromFieldDefinition(fieldDef, vcr, injector, tpl)
1084
- * FromField-->>Render: AngularDynamicOutput
1085
- * Render-->>Client: return AngularDynamicOutput
1086
- */
1087
- render(model, globalProps, vcr, injector, tpl) {
1088
- let result;
1089
- try {
1090
- const fieldDef = this.toFieldDefinition(model, globalProps);
1091
- result = this.fromFieldDefinition(fieldDef, vcr, injector, tpl);
1092
- }
1093
- catch (e) {
1094
- throw new InternalError(`Failed to render Model ${model.constructor.name}: ${e}`);
1095
- }
1096
- return result;
1097
- }
1098
- /**
1099
- * @description Initializes the rendering engine
1100
- * @summary This method initializes the rendering engine. It checks if the engine is already initialized
1101
- * and sets the initialized flag to true. This method is called before the engine is used.
1102
- * @return {Promise<void>} A promise that resolves when initialization is complete
1103
- */
1104
- async initialize() {
1105
- if (this.initialized)
1106
- return;
1107
- this.initialized = true;
1108
- }
1109
- /**
1110
- * @description Registers a component with the rendering engine
1111
- * @summary This static method registers a component constructor with the rendering engine
1112
- * under a specific name. It throws an error if a component is already registered under the same name.
1113
- * @param {string} name - The name to register the component under
1114
- * @param {Constructor<unknown>} constructor - The component constructor
1115
- * @return {void}
1116
- */
1117
- static registerComponent(name, constructor) {
1118
- if (!this._components)
1119
- this._components = {};
1120
- if (name in this._components)
1121
- throw new InternalError(`Component already registered under ${name}`);
1122
- this._components[name] = {
1123
- constructor: constructor,
1124
- };
1125
- }
1126
- /**
1127
- * @description Retrieves registered components from the rendering engine
1128
- * @summary This static method retrieves either all registered components or a specific component
1129
- * by its selector. It throws an error if the requested component is not registered.
1130
- * @param {string} [selector] - Optional selector to retrieve a specific component
1131
- * @return {Object|Array} Either a specific component or an array of all components
1132
- */
1133
- static components(selector) {
1134
- if (!selector)
1135
- return Object.values(this._components);
1136
- if (!(selector in this._components))
1137
- throw new InternalError(`No Component registered under ${selector}`);
1138
- return this._components[selector];
1139
- }
1140
- /**
1141
- * @description Generates a key for reflection metadata
1142
- * @summary This static method generates a key for reflection metadata by prefixing the input key
1143
- * with the Angular engine's reflection prefix. This is used for storing and retrieving metadata.
1144
- * @param {string} key - The base key to prefix
1145
- * @return {string} The prefixed key for reflection metadata
1146
- */
1147
- static key(key) {
1148
- return `${AngularEngineKeys.REFLECT}${key}`;
1149
- }
1150
- }
1151
-
1152
- /**
1153
- * @module engine
1154
- * @description Angular rendering engine for Decaf applications
1155
- * @summary The engine module provides core functionality for rendering Angular components
1156
- * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
1157
- * that enable dynamic component creation, property mapping, and component lifecycle management.
1158
- * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
1159
- * for component configuration.
1160
- */
1161
-
1162
- /**
1163
- * @description Dynamic component renderer for Decaf Angular applications.
1164
- * @summary This component provides a flexible way to dynamically render Angular components
1165
- * at runtime based on a tag name. It handles the creation, property binding, and event
1166
- * subscription for dynamically loaded components. This is particularly useful for
1167
- * building configurable UIs where components need to be determined at runtime.
1168
- *
1169
- * @component {ComponentRendererComponent}
1170
- * @example
1171
- * <ngx-decaf-component-renderer
1172
- * [tag]="tag"
1173
- * [globals]="globals"
1174
- * (listenEvent)="listenEvent($event)">
1175
- * </ngx-decaf-component-renderer>
1176
- *
1177
- * @mermaid
1178
- * classDiagram
1179
- * class ComponentRendererComponent {
1180
- * +ViewContainerRef vcr
1181
- * +string tag
1182
- * +Record~string, unknown~ globals
1183
- * +EnvironmentInjector injector
1184
- * +ComponentRef~unknown~ component
1185
- * +EventEmitter~RendererCustomEvent~ listenEvent
1186
- * +ngOnInit()
1187
- * +ngOnDestroy()
1188
- * +ngOnChanges(changes)
1189
- * -createComponent(tag, globals)
1190
- * -subscribeEvents()
1191
- * -unsubscribeEvents()
1192
- * }
1193
- * ComponentRendererComponent --|> OnInit
1194
- * ComponentRendererComponent --|> OnChanges
1195
- * ComponentRendererComponent --|> OnDestroy
1196
- *
1197
- * @implements {OnInit}
1198
- * @implements {OnChanges}
1199
- * @implements {OnDestroy}
1200
- */
1201
- class ComponentRendererComponent {
1202
- /**
1203
- * @description Creates an instance of ComponentRendererComponent.
1204
- * @summary Initializes a new ComponentRendererComponent. This component doesn't require
1205
- * any dependencies to be injected in its constructor as it uses the inject function to
1206
- * obtain the EnvironmentInjector.
1207
- *
1208
- * @memberOf ComponentRendererComponent
1209
- */
1210
- constructor() {
1211
- /**
1212
- * @description Global properties to pass to the rendered component.
1213
- * @summary This input property allows passing a set of properties to the dynamically
1214
- * rendered component. These properties will be mapped to the component's inputs if they
1215
- * match. Properties that don't match any input on the target component will be filtered out
1216
- * with a warning.
1217
- *
1218
- * @type {Record<string, unknown>}
1219
- * @default {}
1220
- * @memberOf ComponentRendererComponent
1221
- */
1222
- this.globals = {};
1223
- /**
1224
- * @description Injector used for dependency injection in the dynamic component.
1225
- * @summary This injector is used when creating the dynamic component to provide it with
1226
- * access to the application's dependency injection system. It ensures that the dynamically
1227
- * created component can access the same services and dependencies as statically created
1228
- * components.
1229
- *
1230
- * @type {EnvironmentInjector}
1231
- * @memberOf ComponentRendererComponent
1232
- */
1233
- this.injector = inject(EnvironmentInjector);
1234
- /**
1235
- * @description Event emitter for events from the rendered component.
1236
- * @summary This output property emits events that originate from the dynamically rendered
1237
- * component. It allows the parent component to listen for and respond to events from the
1238
- * dynamic component, creating a communication channel between the parent and the dynamically
1239
- * rendered child.
1240
- *
1241
- * @type {EventEmitter<RendererCustomEvent>}
1242
- * @memberOf ComponentRendererComponent
1243
- */
1244
- this.listenEvent = new EventEmitter();
1245
- this.parent = undefined;
1246
- this.logger = getLogger(this);
1247
- }
1248
- /**
1249
- * @description Initializes the component after Angular first displays the data-bound properties.
1250
- * @summary Sets up the component by creating the dynamic component specified by the tag input.
1251
- * This method is called once when the component is initialized and triggers the dynamic
1252
- * component creation process with the provided tag name and global properties.
1253
- *
1254
- * @mermaid
1255
- * sequenceDiagram
1256
- * participant A as Angular Lifecycle
1257
- * participant C as ComponentRendererComponent
1258
- * participant R as NgxRenderingEngine2
1259
- *
1260
- * A->>C: ngOnInit()
1261
- * C->>C: createComponent(tag, globals)
1262
- * C->>R: components(tag)
1263
- * R-->>C: Return component constructor
1264
- * C->>C: Process component inputs
1265
- * C->>C: Create component instance
1266
- * C->>C: subscribeEvents()
1267
- *
1268
- * @return {void}
1269
- * @memberOf ComponentRendererComponent
1270
- */
1271
- ngOnInit() {
1272
- if (!this.parent) {
1273
- this.createComponent(this.tag, this.globals);
1274
- }
1275
- else {
1276
- this.createParentComponent();
1277
- }
1278
- }
1279
- /**
1280
- * @description Cleans up resources when the component is destroyed.
1281
- * @summary Performs cleanup operations when the component is being destroyed by Angular.
1282
- * This includes unsubscribing from all event emitters of the dynamic component and
1283
- * destroying the rendering engine instance to prevent memory leaks.
1284
- *
1285
- * @mermaid
1286
- * sequenceDiagram
1287
- * participant A as Angular Lifecycle
1288
- * participant C as ComponentRendererComponent
1289
- * participant R as NgxRenderingEngine2
1290
- *
1291
- * A->>C: ngOnDestroy()
1292
- * alt component exists
1293
- * C->>C: unsubscribeEvents()
1294
- * C->>R: destroy()
1295
- * end
1296
- *
1297
- * @return {Promise<void>} A promise that resolves when cleanup is complete
1298
- * @memberOf ComponentRendererComponent
1299
- */
1300
- async ngOnDestroy() {
1301
- if (this.component) {
1302
- this.unsubscribeEvents();
1303
- NgxRenderingEngine2$1.destroy();
1304
- }
1305
- }
1306
- /**
1307
- * @description Creates and renders a dynamic component.
1308
- * @summary This method handles the creation of a dynamic component based on the provided tag.
1309
- * It retrieves the component constructor from the rendering engine, processes its inputs,
1310
- * filters out unmapped properties, creates the component instance, and sets up event subscriptions.
1311
- *
1312
- * @param {string} tag - The tag name of the component to create
1313
- * @param {KeyValue} globals - Global properties to pass to the component
1314
- * @return {void}
1315
- *
1316
- * @mermaid
1317
- * sequenceDiagram
1318
- * participant C as ComponentRendererComponent
1319
- * participant R as NgxRenderingEngine2
1320
- * participant V as ViewContainerRef
1321
- *
1322
- * C->>R: components(tag)
1323
- * R-->>C: Return component constructor
1324
- * C->>C: reflectComponentType(component)
1325
- * C->>C: Process input properties
1326
- * C->>C: Filter unmapped properties
1327
- * C->>V: clear()
1328
- * C->>R: createComponent(component, props, metadata, vcr, injector, [])
1329
- * R-->>C: Return component reference
1330
- * C->>C: subscribeEvents()
1331
- *
1332
- * @private
1333
- * @memberOf ComponentRendererComponent
1334
- */
1335
- createComponent(tag, globals = {}) {
1336
- const component = NgxRenderingEngine2$1.components(tag)
1337
- ?.constructor;
1338
- const metadata = reflectComponentType(component);
1339
- const componentInputs = metadata.inputs;
1340
- const props = globals?.['item'] || globals?.['props'] || {};
1341
- if (props?.['tag'])
1342
- delete props['tag'];
1343
- const inputKeys = Object.keys(props);
1344
- const unmappedKeys = [];
1345
- for (const input of inputKeys) {
1346
- if (!inputKeys.length)
1347
- break;
1348
- const prop = componentInputs.find((item) => item.propName === input);
1349
- if (!prop) {
1350
- delete props[input];
1351
- unmappedKeys.push(input);
1352
- }
1353
- }
1354
- this.vcr.clear();
1355
- this.component = NgxRenderingEngine2$1.createComponent(component, props, metadata, this.vcr, this.injector, []);
1356
- this.subscribeEvents();
1357
- }
1358
- createParentComponent() {
1359
- const { component, inputs } = this.parent;
1360
- const metadata = reflectComponentType(component);
1361
- const template = this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes;
1362
- this.component = NgxRenderingEngine2$1.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
1363
- this.subscribeEvents();
1364
- }
1365
- /**
1366
- * @description Subscribes to events emitted by the dynamic component.
1367
- * @summary This method sets up subscriptions to all EventEmitter properties of the
1368
- * dynamically created component. When an event is emitted by the dynamic component,
1369
- * it is captured and re-emitted through the listenEvent output property with additional
1370
- * metadata about the event source.
1371
- *
1372
- * @mermaid
1373
- * sequenceDiagram
1374
- * participant C as ComponentRendererComponent
1375
- * participant D as Dynamic Component
1376
- * participant P as Parent Component
1377
- *
1378
- * C->>C: subscribeEvents()
1379
- * C->>D: Get instance properties
1380
- * loop For each property
1381
- * C->>C: Check if property is EventEmitter
1382
- * alt is EventEmitter
1383
- * C->>D: Subscribe to event
1384
- * D-->>C: Event emitted
1385
- * C->>P: Re-emit event with metadata
1386
- * end
1387
- * end
1388
- *
1389
- * @private
1390
- * @return {void}
1391
- * @memberOf ComponentRendererComponent
1392
- */
1393
- subscribeEvents() {
1394
- if (this.component) {
1395
- const instance = this.component?.instance;
1396
- const componentKeys = Object.keys(instance);
1397
- for (const key of componentKeys) {
1398
- const value = instance[key];
1399
- if (value instanceof EventEmitter)
1400
- instance[key].subscribe((event) => {
1401
- this.listenEvent.emit({
1402
- name: key,
1403
- ...event,
1404
- });
1405
- });
1406
- }
1407
- }
1408
- }
1409
- /**
1410
- * @description Unsubscribes from all events of the dynamic component.
1411
- * @summary This method cleans up event subscriptions when the component is being destroyed.
1412
- * It iterates through all properties of the dynamic component instance and unsubscribes
1413
- * from any EventEmitter properties to prevent memory leaks and unexpected behavior after
1414
- * the component is destroyed.
1415
- *
1416
- * @mermaid
1417
- * sequenceDiagram
1418
- * participant C as ComponentRendererComponent
1419
- * participant D as Dynamic Component
1420
- *
1421
- * C->>C: unsubscribeEvents()
1422
- * C->>D: Get instance properties
1423
- * loop For each property
1424
- * C->>C: Check if property is EventEmitter
1425
- * alt is EventEmitter
1426
- * C->>D: Unsubscribe from event
1427
- * end
1428
- * end
1429
- *
1430
- * @private
1431
- * @return {void}
1432
- * @memberOf ComponentRendererComponent
1433
- */
1434
- unsubscribeEvents() {
1435
- if (this.component) {
1436
- const instance = this.component?.instance;
1437
- const componentKeys = Object.keys(instance);
1438
- for (const key of componentKeys) {
1439
- const value = instance[key];
1440
- if (value instanceof EventEmitter)
1441
- instance[key].unsubscribe();
1442
- }
1443
- }
1444
- }
1445
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1446
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", model: "model", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }, { kind: "ngmodule", type: ForAngularModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); }
1447
- }
1448
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, decorators: [{
1449
- type: Component,
1450
- args: [{ selector: 'ngx-decaf-component-renderer', imports: [ForAngularModule], standalone: true, template: "<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n" }]
1451
- }], ctorParameters: () => [], propDecorators: { vcr: [{
1452
- type: ViewChild,
1453
- args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
1454
- }], tag: [{
1455
- type: Input,
1456
- args: [{ required: true }]
1457
- }], globals: [{
1458
- type: Input
1459
- }], listenEvent: [{
1460
- type: Output
1461
- }], model: [{
1462
- type: Input
1463
- }], parent: [{
1464
- type: Input
1465
- }], inner: [{
1466
- type: ViewChild,
1467
- args: ['inner', { read: TemplateRef, static: true }]
1468
- }] } });
1469
-
1470
- /**
1471
- * @description Component for rendering dynamic models
1472
- * @summary This component is responsible for dynamically rendering models,
1473
- * handling model changes, and managing event subscriptions for the rendered components.
1474
- * It uses the NgxRenderingEngine2 to render the models and supports both string and Model inputs.
1475
- * @class
1476
- * @template M - Type extending Model
1477
- * @param {Injector} injector - Angular Injector for dependency injection
1478
- * @example
1479
- * <ngx-decaf-model-renderer
1480
- * [model]="myModel"
1481
- * [globals]="globalVariables"
1482
- * (listenEvent)="handleEvent($event)">
1483
- * </ngx-decaf-model-renderer>
1484
- * @mermaid
1485
- * sequenceDiagram
1486
- * participant App
1487
- * participant ModelRenderer
1488
- * participant RenderingEngine
1489
- * participant Model
1490
- * App->>ModelRenderer: Input model
1491
- * ModelRenderer->>Model: Parse if string
1492
- * Model-->>ModelRenderer: Parsed model
1493
- * ModelRenderer->>RenderingEngine: Render model
1494
- * RenderingEngine-->>ModelRenderer: Rendered output
1495
- * ModelRenderer->>ModelRenderer: Subscribe to events
1496
- * ModelRenderer-->>App: Emit events
1497
- */
1498
- class ModelRendererComponent {
1499
- constructor() {
1500
- /**
1501
- * @description Global variables to be passed to the rendered component
1502
- */
1503
- this.globals = {};
1504
- /**
1505
- * @description Event emitter for custom events from the rendered component
1506
- */
1507
- this.listenEvent = new EventEmitter();
1508
- this.injector = inject(Injector);
1509
- this.JSON = JSON;
1510
- }
1511
- // constructor() {}
1512
- /**
1513
- * @description Refreshes the rendered model
1514
- * @param {string | M} model - The model to be rendered
1515
- */
1516
- refresh(model) {
1517
- model =
1518
- typeof model === 'string'
1519
- ? Model.build({}, model)
1520
- : model;
1521
- this.output = model.render(this.globals || {}, this.vcr, this.injector, this.inner);
1522
- if (this.output?.inputs)
1523
- this.rendererId = sf(AngularEngineKeys.RENDERED_ID, this.output.inputs['rendererId']);
1524
- this.instance = this.output?.instance;
1525
- this.subscribeEvents();
1526
- }
1527
- /**
1528
- * @description Lifecycle hook that is called when data-bound properties of a directive change
1529
- * @param {SimpleChanges} changes - Object containing changes
1530
- */
1531
- ngOnChanges(changes) {
1532
- if (changes[BaseComponentProps.MODEL]) {
1533
- const { currentValue } = changes[BaseComponentProps.MODEL];
1534
- this.refresh(currentValue);
1535
- }
1536
- }
1537
- /**
1538
- * @description Lifecycle hook that is called when a directive, pipe, or service is destroyed
1539
- * @return {Promise<void>}
1540
- */
1541
- async ngOnDestroy() {
1542
- if (this.instance) {
1543
- this.unsubscribeEvents();
1544
- await NgxRenderingEngine2.destroy();
1545
- }
1546
- this.output = undefined;
1547
- }
1548
- subscribeEvents() {
1549
- if (this.instance) {
1550
- const componentKeys = Object.keys(this.instance);
1551
- for (const key of componentKeys) {
1552
- const value = this.instance[key];
1553
- if (value instanceof EventEmitter)
1554
- this.instance[key].subscribe((event) => {
1555
- this.listenEvent.emit({
1556
- component: this.output?.component.name || '',
1557
- name: key,
1558
- ...event,
1559
- });
1560
- });
1561
- }
1562
- }
1563
- }
1564
- /**
1565
- * @description Unsubscribes from events emitted by the rendered component
1566
- */
1567
- unsubscribeEvents() {
1568
- if (this.instance) {
1569
- const componentKeys = Object.keys(this.instance);
1570
- for (const key of componentKeys) {
1571
- const value = this.instance[key];
1572
- if (value instanceof EventEmitter)
1573
- this.instance[key].unsubscribe();
1574
- }
1575
- }
1576
- }
1577
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ModelRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1578
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ModelRendererComponent, isStandalone: true, selector: "ngx-decaf-model-renderer", inputs: { model: "model", globals: "globals", rendererId: "rendererId" }, outputs: { listenEvent: "listenEvent" }, viewQueries: [{ propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }, { propertyName: "vcr", first: true, predicate: ["componentOuter"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: " <ng-template #componentOuter></ng-template>\n <ng-template #inner>\n <div [id]=\"rendererId || null\">\n @for (child of output?.children; track child) {\n @if(child?.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\" />\n } @else {\n <ng-container\n #childComponents\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n </div>\n </ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ForAngularModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }] }); }
1579
- }
1580
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ModelRendererComponent, decorators: [{
1581
- type: Component,
1582
- args: [{ standalone: true, imports: [ForAngularModule, NgComponentOutlet, ComponentRendererComponent], selector: 'ngx-decaf-model-renderer', template: " <ng-template #componentOuter></ng-template>\n <ng-template #inner>\n <div [id]=\"rendererId || null\">\n @for (child of output?.children; track child) {\n @if(child?.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\" />\n } @else {\n <ng-container\n #childComponents\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n </div>\n </ng-template>\n" }]
1583
- }], propDecorators: { model: [{
1584
- type: Input,
1585
- args: [{ required: true }]
1586
- }], globals: [{
1587
- type: Input
1588
- }], inner: [{
1589
- type: ViewChild,
1590
- args: ['inner', { read: TemplateRef, static: true }]
1591
- }], rendererId: [{
1592
- type: Input
1593
- }], vcr: [{
1594
- type: ViewChild,
1595
- args: ['componentOuter', { static: true, read: ViewContainerRef }]
1596
- }], listenEvent: [{
1597
- type: Output
1598
- }] } });
1599
-
1600
- /**
1601
- * @class NgxCrudFormField
1602
- * @implements {FieldProperties}
1603
- * @implements {ControlValueAccessor}
1604
- * @summary Abstract class representing a CRUD form field for Angular applications
1605
- * @description This class provides the base implementation for CRUD form fields in Angular,
1606
- * implementing both CrudFormField and ControlValueAccessor interfaces.
1607
- */
1608
- class NgxCrudFormField {
1609
- constructor() {
1610
- this.translateService = inject(TranslateService);
1611
- // protected constructor() {}
1612
- /**
1613
- * @summary String formatting function
1614
- * @description Provides access to the sf function for error message formatting
1615
- * @prop {function(string, ...string): string} sf - String formatting function
1616
- */
1617
- this.sf = sf;
1618
- /**
1619
- * @summary Change callback function
1620
- * @description Function called when the field value changes
1621
- * @property {function(): unknown} onChange - onChange event handler
1622
- */
1623
- this.onChange = () => { };
1624
- /**
1625
- * @summary Touch callback function
1626
- * @description Function called when the field is touched
1627
- * @property {function(): unknown} onTouch - onTouch event handler
1628
- */
1629
- this.onTouch = () => { };
1630
- }
1631
- /**
1632
- * @summary Write value to the field
1633
- * @description Sets the value of the field
1634
- * @param {string} obj - The value to be set
1635
- */
1636
- writeValue(obj) {
1637
- this.value = obj;
1638
- }
1639
- /**
1640
- * @summary Register change callback
1641
- * @description Registers a function to be called when the field value changes
1642
- * @param {function(): unknown} fn - The function to be called on change
1643
- */
1644
- registerOnChange(fn) {
1645
- this.onChange = fn;
1646
- }
1647
- /**
1648
- * @summary Register touch callback
1649
- * @description Registers a function to be called when the field is touched
1650
- * @param {function(): unknown} fn - The function to be called on touch
1651
- */
1652
- registerOnTouched(fn) {
1653
- this.onTouch = fn;
1654
- }
1655
- /**
1656
- * @summary Set disabled state
1657
- * @description Sets the disabled state of the field
1658
- * @param {boolean} isDisabled - Whether the field should be disabled
1659
- */
1660
- setDisabledState(isDisabled) {
1661
- this.disabled = isDisabled;
1662
- }
1663
- /**
1664
- * @summary After view initialization logic
1665
- * @description Performs necessary setup after the view has been initialized
1666
- * @returns {HTMLElement} The parent element of the field
1667
- */
1668
- afterViewInit() {
1669
- let parent;
1670
- switch (this.operation) {
1671
- case OperationKeys.READ:
1672
- case OperationKeys.DELETE:
1673
- return this.component.nativeElement.parentElement;
1674
- case OperationKeys.CREATE:
1675
- case OperationKeys.UPDATE:
1676
- try {
1677
- parent = NgxFormService.getParentEl(this.component.nativeElement, 'div');
1678
- }
1679
- catch (e) {
1680
- throw new RenderingError(`Unable to retrieve parent form element for the ${this.operation}: ${e instanceof Error ? e.message : e}`);
1681
- }
1682
- // NgxFormService.register(parent.id, this.formGroup, this as AngularFieldDefinition);
1683
- return parent;
1684
- default:
1685
- throw new InternalError(`Invalid operation: ${this.operation}`);
1686
- }
1687
- }
1688
- /**
1689
- * @summary Cleanup on component destruction
1690
- * @description Unregisters the field when the component is destroyed
1691
- */
1692
- onDestroy() {
1693
- if (this.formGroup)
1694
- NgxFormService.unregister(this.formGroup);
1695
- }
1696
- /**
1697
- * @summary Get field errors
1698
- * @description Retrieves all errors associated with the field
1699
- * @returns {string|void} An array of error objects
1700
- */
1701
- getErrors(parent) {
1702
- const formControl = this.formControl;
1703
- if ((!formControl.pristine || formControl.touched) && !formControl.valid) {
1704
- const collapsableContainer = parent.closest('ion-accordion-group');
1705
- if (collapsableContainer)
1706
- collapsableContainer.setAttribute('value', 'open');
1707
- const errors = Object.keys(formControl.errors ?? {}).map(key => ({
1708
- key: key,
1709
- message: key,
1710
- }));
1711
- for (const error of errors)
1712
- return `* ${this.sf(this.translateService.instant(`errors.${error?.['message']}`), this[error?.['key']] ?? "")}`;
1713
- }
1714
- }
1715
- }
1716
-
1717
- /**
1718
- * @description A dynamic form field component for CRUD operations.
1719
- * @summary The CrudFieldComponent is a versatile form field component that adapts to different
1720
- * input types and CRUD operations. It extends NgxCrudFormField to inherit form handling capabilities
1721
- * and implements lifecycle hooks to properly initialize, render, and clean up. This component
1722
- * supports various input types (text, number, date, select, etc.), validation rules, and styling
1723
- * options, making it suitable for building dynamic forms for create, read, update, and delete
1724
- * operations.
1725
- *
1726
- * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
1727
- * @param {string} name - The field name, used as form control identifier
1728
- * @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
1729
- * @param {string|number|Date} value - The initial value of the field
1730
- * @param {boolean} disabled - Whether the field is disabled
1731
- * @param {string} label - The display label for the field
1732
- * @param {string} placeholder - Placeholder text when field is empty
1733
- * @param {string} format - Format pattern for the field value
1734
- * @param {boolean} hidden - Whether the field should be hidden
1735
- * @param {number|Date} max - Maximum allowed value
1736
- * @param {number} maxlength - Maximum allowed length
1737
- * @param {number|Date} min - Minimum allowed value
1738
- * @param {number} minlength - Minimum allowed length
1739
- * @param {string} pattern - Validation pattern
1740
- * @param {boolean} readonly - Whether the field is read-only
1741
- * @param {boolean} required - Whether the field is required
1742
- * @param {number} step - Step value for number inputs
1743
- * @param {FormGroup} formGroup - The parent form group
1744
- * @param {StringOrBoolean} translatable - Whether field labels should be translated
1745
- *
1746
- * @component CrudFieldComponent
1747
- * @example
1748
- * <ngx-decaf-crud-field
1749
- * operation="create"
1750
- * name="firstName"
1751
- * type="text"
1752
- * label="<NAME>"
1753
- * placeholder="<NAME>"
1754
- * [value]="model.firstName"
1755
- * [disabled]="model.readOnly">
1756
- *
1757
- *
1758
- * @memberOf module:for-angular
1759
- */
1760
- let CrudFieldComponent = class CrudFieldComponent extends NgxCrudFormField {
1761
- constructor() {
1762
- super(...arguments);
1763
- /**
1764
- * @description The parent field path, if this field is nested.
1765
- * @summary Specifies the full dot-delimited path of the parent field. This is only set when the field is nested.
1766
- *
1767
- * @type {string}
1768
- * @memberOf CrudFieldComponent
1769
- */
1770
- this.childOf = '';
1771
- /**
1772
- * @description The initial value of the field.
1773
- * @summary Sets the initial value of the form field. This can be a string, number, or Date
1774
- * depending on the field type. For select fields, this should match one of the option values.
1775
- *
1776
- * @type {string | number | Date}
1777
- * @default ''
1778
- * @memberOf CrudFieldComponent
1779
- */
1780
- this.value = '';
1781
- /**
1782
- * @description Interface style for select inputs.
1783
- * @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
1784
- * This determines how the select options are presented to the user.
1785
- *
1786
- * @type {SelectInterface}
1787
- * @memberOf CrudFieldComponent
1788
- */
1789
- this.interface = 'popover';
1790
- /**
1791
- * @description Spellcheck attribute for text inputs.
1792
- * @summary Enables or disables spellchecking for text inputs.
1793
- * When true, the browser will check the spelling of the input text.
1794
- *
1795
- * @type {boolean}
1796
- * @default false
1797
- * @memberOf CrudFieldComponent
1798
- */
1799
- this.spellcheck = false;
1800
- /**
1801
- * @description Input mode for text inputs.
1802
- * @summary Hints at the type of data that might be entered by the user while editing the element.
1803
- * This can affect the virtual keyboard layout on mobile devices.
1804
- *
1805
- * @type {'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'}
1806
- * @default 'none'
1807
- * @memberOf CrudFieldComponent
1808
- */
1809
- this.inputmode = 'none';
1810
- /**
1811
- * @description Autocomplete behavior for the field.
1812
- * @summary Specifies whether and how the browser should automatically complete the input.
1813
- * This can improve user experience by suggesting previously entered values.
1814
- *
1815
- * @type {AutocompleteTypes}
1816
- * @default 'off'
1817
- * @memberOf CrudFieldComponent
1818
- */
1819
- this.autocomplete = 'off';
1820
- /**
1821
- * @description Fill style for the field.
1822
- * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
1823
- * This affects the border and background of the field.
1824
- *
1825
- * @type {'outline' | 'solid'}
1826
- * @default 'outline'
1827
- * @memberOf CrudFieldComponent
1828
- */
1829
- this.fill = 'outline';
1830
- /**
1831
- * @description Placement of the label relative to the field.
1832
- * @summary Specifies where the label should be placed relative to the field.
1833
- * Options include 'start', 'end', 'floating', 'stacked', and 'fixed'.
1834
- *
1835
- * @type {'start' | 'end' | 'floating' | 'stacked' | 'fixed'}
1836
- * @default 'floating'
1837
- * @memberOf CrudFieldComponent
1838
- */
1839
- this.labelPlacement = 'floating';
1840
- /**
1841
- * @description Update mode for the field.
1842
- * @summary Determines when the field value should be updated in the form model.
1843
- * Options include 'change', 'blur', and 'submit'.
1844
- *
1845
- * @type {FieldUpdateMode}
1846
- * @default 'change'
1847
- * @memberOf CrudFieldComponent
1848
- */
1849
- this.updateOn = 'change';
1850
- /**
1851
- * @description Translatability of field labels.
1852
- * @summary Indicates whether the field labels should be translated based on the current language settings.
1853
- * This is useful for applications supporting multiple languages.
1854
- *
1855
- * @type {StringOrBoolean}
1856
- * @default true
1857
- * @memberOf CrudFieldComponent
1858
- */
1859
- this.translatable = true;
1860
- }
1861
- // constructor() {
1862
- // }
1863
- ngOnInit() {
1864
- // super.onInit(this.updateOn);
1865
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
1866
- this.formGroup = undefined;
1867
- }
1868
- else {
1869
- if (this.type === HTML5InputTypes.RADIO && !this.value)
1870
- this.formGroup?.get(this.name)?.setValue(this.options[0].value); // TODO: migrate to RenderingEngine
1871
- }
1872
- }
1873
- ngAfterViewInit() {
1874
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
1875
- super.afterViewInit();
1876
- }
1877
- ngOnDestroy() {
1878
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
1879
- this.onDestroy();
1880
- }
1881
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1882
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", path: "path", childOf: "childOf", type: "type", value: "value", disabled: "disabled", label: "label", placeholder: "placeholder", format: "format", hidden: "hidden", max: "max", maxlength: "maxlength", min: "min", minlength: "minlength", pattern: "pattern", readonly: "readonly", required: "required", step: "step", equals: "equals", different: "different", lessThan: "lessThan", lessThanOrEqual: "lessThanOrEqual", greaterThan: "greaterThan", greaterThanOrEqual: "greaterThanOrEqual", cols: "cols", rows: "rows", alignment: "alignment", checked: "checked", justify: "justify", cancelText: "cancelText", interface: "interface", options: "options", mode: "mode", spellcheck: "spellcheck", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", translatable: "translatable", uid: "uid" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container #component [formGroup]=\"formGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [label]=\"translatable ? (label | translate) : label\"\n >\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n [name]=\"path\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n >\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [name]=\"path\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [name]=\"path\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\">\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ translatable ? (option.text | translate) : options.text }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [name]=\"path\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"translatable ? (label | translate) : label\">\n </ion-input>\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-hover: var(--ion-color-primary);--background-activated-opacity: .15;--background-focused: var(--ion-color-primary);--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--ion-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;border:2px solid var(--ion-color-primary);padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--ion-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--ion-color-gray-8)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: ForAngularModule }, { kind: "component", type: i1$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
1883
- };
1884
- CrudFieldComponent = __decorate([
1885
- Dynamic()
1886
- ], CrudFieldComponent);
1887
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrudFieldComponent, decorators: [{
1888
- type: Component,
1889
- args: [{ standalone: true, imports: [
1890
- ForAngularModule,
1891
- IonInput,
1892
- IonItem,
1893
- IonCheckbox,
1894
- IonRadioGroup,
1895
- IonRadio,
1896
- IonSelect,
1897
- IonSelectOption,
1898
- IonLabel,
1899
- IonText,
1900
- IonTextarea,
1901
- ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container #component [formGroup]=\"formGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [label]=\"translatable ? (label | translate) : label\"\n >\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n [name]=\"path\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n >\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [name]=\"path\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [name]=\"path\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\">\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ translatable ? (option.text | translate) : options.text }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [name]=\"path\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"translatable ? (label | translate) : label\">\n </ion-input>\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-hover: var(--ion-color-primary);--background-activated-opacity: .15;--background-focused: var(--ion-color-primary);--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--ion-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;border:2px solid var(--ion-color-primary);padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--ion-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--ion-color-gray-8)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}\n"] }]
1902
- }], propDecorators: { operation: [{
1903
- type: Input,
1904
- args: [{ required: true }]
1905
- }], name: [{
1906
- type: Input,
1907
- args: [{ required: true }]
1908
- }], path: [{
1909
- type: Input,
1910
- args: [{ required: true }]
1911
- }], childOf: [{
1912
- type: Input
1913
- }], type: [{
1914
- type: Input,
1915
- args: [{ required: true }]
1916
- }], value: [{
1917
- type: Input
1918
- }], disabled: [{
1919
- type: Input
1920
- }], label: [{
1921
- type: Input,
1922
- args: [{ required: true }]
1923
- }], placeholder: [{
1924
- type: Input
1925
- }], format: [{
1926
- type: Input
1927
- }], hidden: [{
1928
- type: Input
1929
- }], max: [{
1930
- type: Input
1931
- }], maxlength: [{
1932
- type: Input
1933
- }], min: [{
1934
- type: Input
1935
- }], minlength: [{
1936
- type: Input
1937
- }], pattern: [{
1938
- type: Input
1939
- }], readonly: [{
1940
- type: Input
1941
- }], required: [{
1942
- type: Input
1943
- }], step: [{
1944
- type: Input
1945
- }], equals: [{
1946
- type: Input
1947
- }], different: [{
1948
- type: Input
1949
- }], lessThan: [{
1950
- type: Input
1951
- }], lessThanOrEqual: [{
1952
- type: Input
1953
- }], greaterThan: [{
1954
- type: Input
1955
- }], greaterThanOrEqual: [{
1956
- type: Input
1957
- }], cols: [{
1958
- type: Input
1959
- }], rows: [{
1960
- type: Input
1961
- }], alignment: [{
1962
- type: Input
1963
- }], checked: [{
1964
- type: Input
1965
- }], justify: [{
1966
- type: Input
1967
- }], cancelText: [{
1968
- type: Input
1969
- }], interface: [{
1970
- type: Input
1971
- }], options: [{
1972
- type: Input
1973
- }], mode: [{
1974
- type: Input
1975
- }], spellcheck: [{
1976
- type: Input
1977
- }], inputmode: [{
1978
- type: Input
1979
- }], autocomplete: [{
1980
- type: Input
1981
- }], fill: [{
1982
- type: Input
1983
- }], labelPlacement: [{
1984
- type: Input
1985
- }], updateOn: [{
1986
- type: Input
1987
- }], component: [{
1988
- type: ViewChild,
1989
- args: ['component', { read: ElementRef }]
1990
- }], formGroup: [{
1991
- type: Input
1992
- }], formControl: [{
1993
- type: Input
1994
- }], translatable: [{
1995
- type: Input
1996
- }], uid: [{
1997
- type: Input
1998
- }] } });
1999
-
2000
- const CssClasses = {
2001
- BUTTONS_CONTAINER: 'buttons-container',
2002
- };
2003
- const DefaultFormReactiveOptions = {
2004
- buttons: {
2005
- submit: {
2006
- text: 'Submit',
2007
- },
2008
- clear: {
2009
- text: 'Clear',
2010
- },
2011
- },
2012
- };
2013
-
2014
- /**
2015
- * @component CrudFormComponent
2016
- * @example <ngx-decaf-crud-form
2017
- * action="create"
2018
- * operation="create"
2019
- * formGroup="formGroup"
2020
- * rendererId="rendererId"
2021
- * submitEvent="submitEvent"
2022
- * target="_self"
2023
- * method="event">
2024
- * </ngx-decaf-crud-form>
2025
- *
2026
- * @param {string} action - The action to be performed (create, read, update, delete)
2027
- * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
2028
- * @param {FormGroup} formGroup - The form group
2029
- * @param {string} rendererId - The renderer id
2030
- * @param {SubmitEvent} submitEvent - The submit event
2031
- * @param {string} target - The target
2032
- * @param {string} method - The method
2033
- */
2034
- let CrudFormComponent = class CrudFormComponent {
2035
- constructor() {
2036
- this.updateOn = 'change';
2037
- this.target = '_self';
2038
- this.method = 'event';
2039
- this.submitEvent = new EventEmitter();
2040
- /**
2041
- * @description Angular Location service.
2042
- * @summary Injected service that provides access to the browser's URL and history.
2043
- * This service is used for interacting with the browser's history API, allowing
2044
- * for back navigation and URL manipulation outside of Angular's router.
2045
- *
2046
- * @private
2047
- * @type {Location}
2048
- * @memberOf CrudFormComponent
2049
- */
2050
- this.location = inject(Location);
2051
- this.OperationKeys = OperationKeys;
2052
- }
2053
- // ngAfterViewInit() {
2054
- // if (![OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
2055
- // NgxFormService.formAfterViewInit(this, this.rendererId);
2056
- // }
2057
- async ngOnInit() {
2058
- if (!this.logger)
2059
- this.logger = getLogger(this);
2060
- if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
2061
- this.formGroup = undefined;
2062
- this.options = Object.assign({}, DefaultFormReactiveOptions, this.options || {});
2063
- }
2064
- ngOnDestroy() {
2065
- if (this.formGroup)
2066
- NgxFormService.unregister(this.formGroup);
2067
- }
2068
- /**
2069
- * @param {SubmitEvent} event
2070
- */
2071
- async submit(event) {
2072
- event.preventDefault();
2073
- event.stopImmediatePropagation();
2074
- event.stopPropagation();
2075
- if (!NgxFormService.validateFields(this.formGroup))
2076
- return false;
2077
- const data = NgxFormService.getFormData(this.formGroup);
2078
- this.submitEvent.emit({
2079
- data,
2080
- component: 'CrudFormComponent',
2081
- name: this.action || EventConstants.SUBMIT_EVENT,
2082
- handlers: this.handlers,
2083
- });
2084
- }
2085
- handleReset() {
2086
- this.location.back();
2087
- // if(OperationKeys.DELETE !== this.operation)
2088
- // NgxFormService.reset(this.formGroup);
2089
- // else
2090
- // this.location.back();
2091
- }
2092
- handleDelete() {
2093
- this.submitEvent.emit({
2094
- data: this.uid,
2095
- component: 'CrudFormComponent',
2096
- name: EventConstants.SUBMIT_EVENT,
2097
- });
2098
- }
2099
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrudFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2100
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", inputs: { model: "model", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", childOf: "childOf", rendererId: "rendererId", uid: "uid" }, outputs: { submitEvent: "submitEvent" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["reactiveForm"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-flex dcf-flex-right\">\n <div>\n @if(options.buttons.clear) {\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{operation === 'update' ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n }\n\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-flex dcf-flex-right ' + operation\" id=\"dcf-buttons-container\">\n <div>\n @if(options.buttons.clear) {\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ ['delete', 'read', 'update'].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n }\n @if(operation === 'delete' && uid) {\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n }\n\n </div>\n </div>\n}\n\n", styles: ["#dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}\n"], dependencies: [{ kind: "ngmodule", type: ForAngularModule }, { kind: "component", type: i1$1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
2101
- };
2102
- CrudFormComponent = __decorate([
2103
- Dynamic()
2104
- ], CrudFormComponent);
2105
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CrudFormComponent, decorators: [{
2106
- type: Component,
2107
- args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [ForAngularModule, IonIcon], template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-flex dcf-flex-right\">\n <div>\n @if(options.buttons.clear) {\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{operation === 'update' ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n }\n\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-flex dcf-flex-right ' + operation\" id=\"dcf-buttons-container\">\n <div>\n @if(options.buttons.clear) {\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ ['delete', 'read', 'update'].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n }\n @if(operation === 'delete' && uid) {\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n }\n\n </div>\n </div>\n}\n\n", styles: ["#dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}\n"] }]
2108
- }], propDecorators: { model: [{
2109
- type: Input
2110
- }], updateOn: [{
2111
- type: Input
2112
- }], component: [{
2113
- type: ViewChild,
2114
- args: ['reactiveForm', { static: false, read: ElementRef }]
2115
- }], target: [{
2116
- type: Input
2117
- }], method: [{
2118
- type: Input
2119
- }], options: [{
2120
- type: Input
2121
- }], action: [{
2122
- type: Input
2123
- }], operation: [{
2124
- type: Input,
2125
- args: [{ required: true }]
2126
- }], handlers: [{
2127
- type: Input
2128
- }], formGroup: [{
2129
- type: Input
2130
- }], childOf: [{
2131
- type: Input
2132
- }], rendererId: [{
2133
- type: Input
2134
- }], uid: [{
2135
- type: Input
2136
- }], submitEvent: [{
2137
- type: Output
2138
- }] } });
2139
-
2140
- /**
2141
- * @description Angular integration for the Decaf framework
2142
- * @summary This module provides Angular components and services for integrating with the Decaf framework.
2143
- * It includes components for rendering models, CRUD operations, and form handling, as well as
2144
- * rendering engines and utility functions to facilitate Angular application development with Decaf.
2145
- * @module for-angular
2146
- */
2147
-
2148
- /**
2149
- * Generated bundle index. Do not edit.
2150
- */
2151
-
2152
- export { AngularEngineKeys, BaseComponentProps, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, Dynamic, DynamicModule, EventConstants, FormConstants, LoggerLevels, ModelRendererComponent, NgxRenderingEngine, NgxRenderingEngine2, RouteDirections };
2153
- //# sourceMappingURL=decaf-ts-for-angular.mjs.map