@decaf-ts/for-angular 0.0.37 → 0.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/decaf-ts-for-angular.mjs +1520 -1089
- package/fesm2022/decaf-ts-for-angular.mjs.map +1 -1
- package/index.d.ts +921 -635
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { UIKeys, HTML5InputTypes, parseValueByType, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, RenderingError, UIMediaBreakPoints } from '@decaf-ts/ui-decorators';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, ChangeDetectorRef, Renderer2, EventEmitter, ElementRef, Input, Output, ViewChild, Inject, Directive, EnvironmentInjector, ViewContainerRef, TemplateRef, ViewEncapsulation, Component, HostListener, CUSTOM_ELEMENTS_SCHEMA, Injector } from '@angular/core';
|
|
3
|
+
import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, ChangeDetectorRef, Renderer2, EventEmitter, ElementRef, Input, Output, ViewChild, Inject, Directive, NgZone, EnvironmentInjector, ViewContainerRef, TemplateRef, ViewEncapsulation, Component, HostListener, CUSTOM_ELEMENTS_SCHEMA, Injector } from '@angular/core';
|
|
4
4
|
import { CommonModule, Location, NgComponentOutlet } from '@angular/common';
|
|
5
5
|
import { VALIDATION_PARENT_KEY, ValidationKeys, DEFAULT_PATTERNS, Validation, Primitives, ComparisonValidationKeys, PathProxyEngine, Model, ModelKeys, isValidDate as isValidDate$1, parseDate, sf, ReservedModels } from '@decaf-ts/decorator-validation';
|
|
6
|
-
import { InternalError, OperationKeys } from '@decaf-ts/db-decorators';
|
|
6
|
+
import { InternalError, OperationKeys, NotFoundError } from '@decaf-ts/db-decorators';
|
|
7
7
|
import * as i1 from '@angular/forms';
|
|
8
8
|
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule, FormArray, AbstractControl, Validators } from '@angular/forms';
|
|
9
9
|
import { InjectableRegistryImp } from '@decaf-ts/injectable-decorators';
|
|
@@ -12,16 +12,16 @@ import { Logging, LoggedClass } from '@decaf-ts/logging';
|
|
|
12
12
|
import { uses, Repository, OrderDirection, Condition } from '@decaf-ts/core';
|
|
13
13
|
import { apply, metadata } from '@decaf-ts/reflection';
|
|
14
14
|
import { Router, NavigationEnd, NavigationStart } from '@angular/router';
|
|
15
|
-
import { forkJoin, firstValueFrom, fromEvent,
|
|
15
|
+
import { forkJoin, firstValueFrom, Subject, BehaviorSubject, fromEvent, Observable, debounceTime, timer } from 'rxjs';
|
|
16
16
|
import { provideHttpClient, HttpClient } from '@angular/common/http';
|
|
17
|
-
import { map } from 'rxjs/operators';
|
|
18
|
-
import { MenuController
|
|
17
|
+
import { map, takeUntil } from 'rxjs/operators';
|
|
18
|
+
import { MenuController } from '@ionic/angular';
|
|
19
19
|
import { Title, DomSanitizer } from '@angular/platform-browser';
|
|
20
20
|
import { __decorate, __metadata } from 'tslib';
|
|
21
21
|
import { IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonText, IonTextarea, IonButton, IonIcon, IonCard, IonCardContent, IonAccordionGroup, IonAccordion, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonContent, IonPopover } from '@ionic/angular/standalone';
|
|
22
22
|
import { addIcons } from 'ionicons';
|
|
23
23
|
import * as allIcons from 'ionicons/icons';
|
|
24
|
-
import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
|
|
24
|
+
import { chevronUpOutline, chevronDownOutline, createOutline, trashOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* @description Angular engine key constants.
|
|
@@ -405,21 +405,98 @@ class ValidatorFactory {
|
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
/**
|
|
408
|
-
* @module
|
|
408
|
+
* @module lib/for-angular-common.module
|
|
409
409
|
* @description Core Angular module and providers for Decaf's for-angular package.
|
|
410
410
|
* @summary Provides the shared Angular module, injection tokens and helper functions used
|
|
411
411
|
* by the for-angular integration. This module wires up common imports (forms, translation)
|
|
412
412
|
* and exposes helper providers such as DB adapter registration and logger utilities.
|
|
413
|
-
*
|
|
414
413
|
* @link {@link ForAngularCommonModule}
|
|
415
414
|
*/
|
|
416
|
-
/** */
|
|
417
415
|
const DB_ADAPTER_PROVIDER = 'DB_ADAPTER_PROVIDER';
|
|
418
|
-
|
|
416
|
+
/**
|
|
417
|
+
* @description Injection token for registering the database adapter provider.
|
|
418
|
+
* @summary Used to inject the database adapter instance that implements DecafRepositoryAdapter.
|
|
419
|
+
* This token allows the framework to locate and use the application's specific database implementation.
|
|
420
|
+
* @const {InjectionToken<DecafRepositoryAdapter>}
|
|
421
|
+
* @memberOf module:lib/for-angular-common.module
|
|
422
|
+
*/
|
|
423
|
+
const DB_ADAPTER_PROVIDER_TOKEN = new InjectionToken('DB_ADAPTER_PROVIDER_TOKEN');
|
|
424
|
+
/**
|
|
425
|
+
* @description Injection token for the root path of locale translation files.
|
|
426
|
+
* @summary Used to configure the base path where i18n translation files are located.
|
|
427
|
+
* This allows the translation loader to locate JSON files for different languages.
|
|
428
|
+
* @const {InjectionToken<string>}
|
|
429
|
+
* @memberOf module:lib/for-angular-common.module
|
|
430
|
+
* @example
|
|
431
|
+
* // Typical usage when providing the token
|
|
432
|
+
* { provide: LOCALE_ROOT_TOKEN, useValue: './assets/i18n/' }
|
|
433
|
+
*/
|
|
419
434
|
const LOCALE_ROOT_TOKEN = new InjectionToken('LOCALE_ROOT_TOKEN');
|
|
420
|
-
/* Generic token for injecting on class
|
|
435
|
+
/* Generic token for injecting on class constructors */
|
|
436
|
+
/**
|
|
437
|
+
* @description Generic injection token for providing arbitrary values to constructors.
|
|
438
|
+
* @summary Used to inject classes, strings, or any other value into component or service constructors.
|
|
439
|
+
* This is a flexible token that can be used to provide any type of dependency when more specific
|
|
440
|
+
* tokens are not appropriate. The actual type and purpose of the injected value is determined by
|
|
441
|
+
* the provider configuration.
|
|
442
|
+
* @const {InjectionToken<unknown>}
|
|
443
|
+
* @memberOf module:lib/for-angular-common.module
|
|
444
|
+
* @example
|
|
445
|
+
* // Inject a string value
|
|
446
|
+
* { provide: CPTKN, useValue: 'some-config-value' }
|
|
447
|
+
*
|
|
448
|
+
* // Inject a class
|
|
449
|
+
* { provide: CPTKN, useClass: MyService }
|
|
450
|
+
*
|
|
451
|
+
* // Inject any arbitrary value
|
|
452
|
+
* { provide: CPTKN, useValue: { key: 'value', data: [1, 2, 3] } }
|
|
453
|
+
*/
|
|
421
454
|
const CPTKN = new InjectionToken('CPTKN', { providedIn: 'root', factory: () => '' });
|
|
455
|
+
/**
|
|
456
|
+
* @description Injection token for i18n resource configuration.
|
|
457
|
+
* @summary Used to provide configuration for internationalization resources, including
|
|
458
|
+
* translation file locations and supported languages. This token configures how the
|
|
459
|
+
* application loads and manages translation resources.
|
|
460
|
+
* @const {InjectionToken<{resources: I18nResourceConfig[]; versionedSuffix: boolean}>}
|
|
461
|
+
* @memberOf module:lib/for-angular-common.module
|
|
462
|
+
*/
|
|
422
463
|
const I18N_CONFIG_TOKEN = new InjectionToken('I18N_CONFIG_TOKEN');
|
|
464
|
+
/**
|
|
465
|
+
* @description Provides an array of component types for dynamic rendering.
|
|
466
|
+
* @summary Helper function to package component constructors for registration with the
|
|
467
|
+
* rendering engine. This function accepts component classes and returns them as an array
|
|
468
|
+
* suitable for use with the CPTKN injection token.
|
|
469
|
+
* @param {...Constructor<unknown>[]} components - Component constructor classes to register
|
|
470
|
+
* @return {Constructor<unknown>[]} Array of component constructors
|
|
471
|
+
* @memberOf module:lib/for-angular-common.module
|
|
472
|
+
* @example
|
|
473
|
+
* // Register multiple custom components
|
|
474
|
+
* providers: [
|
|
475
|
+
* { provide: CPTKN, useValue: provideDynamicComponents(MyComponent, AnotherComponent) }
|
|
476
|
+
* ]
|
|
477
|
+
*/
|
|
478
|
+
function provideDynamicComponents(...components) {
|
|
479
|
+
return components;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* @description Retrieves the repository instance for a given model.
|
|
483
|
+
* @summary Creates or retrieves a DecafRepository instance for the specified model. This function
|
|
484
|
+
* resolves the model by name or class, locates the registered database adapter, and returns
|
|
485
|
+
* a fully initialized repository instance for performing CRUD operations.
|
|
486
|
+
* @param {Model | string} model - The model class or model name string
|
|
487
|
+
* @return {DecafRepository<Model>} Repository instance for the model
|
|
488
|
+
* @throws {InternalError} If model is not found or not registered with @model decorator
|
|
489
|
+
* @memberOf module:lib/for-angular-common.module
|
|
490
|
+
* @example
|
|
491
|
+
* // Get repository by model class
|
|
492
|
+
* const userRepo = getModelRepository(User);
|
|
493
|
+
*
|
|
494
|
+
* // Get repository by model name
|
|
495
|
+
* const productRepo = getModelRepository('Product');
|
|
496
|
+
*
|
|
497
|
+
* // Use repository for queries
|
|
498
|
+
* const users = await userRepo.findAll();
|
|
499
|
+
*/
|
|
423
500
|
function getModelRepository(model) {
|
|
424
501
|
try {
|
|
425
502
|
const modelName = (typeof model === Primitives.STRING ? model : model.constructor.name);
|
|
@@ -437,8 +514,28 @@ function getModelRepository(model) {
|
|
|
437
514
|
throw new InternalError(error?.message || error);
|
|
438
515
|
}
|
|
439
516
|
}
|
|
440
|
-
|
|
441
|
-
|
|
517
|
+
/**
|
|
518
|
+
* @description Provides a database adapter for dependency injection.
|
|
519
|
+
* @summary Creates an Angular provider that registers a database adapter instance. This function
|
|
520
|
+
* instantiates the adapter class, registers its flavour globally, and returns a provider object
|
|
521
|
+
* for use in Angular's dependency injection system.
|
|
522
|
+
* @template DbAdapter - The database adapter class type extending {flavour: string}
|
|
523
|
+
* @param {Constructor<DbAdapter>} adapterClass - Database adapter constructor class
|
|
524
|
+
* @param {KeyValue} [options={}] - Configuration options passed to adapter constructor
|
|
525
|
+
* @param {string} [flavour] - Optional flavour override; uses adapter.flavour if not provided
|
|
526
|
+
* @return {Provider} Angular provider object for DB_ADAPTER_PROVIDER_TOKEN
|
|
527
|
+
* @memberOf module:lib/for-angular-common.module
|
|
528
|
+
* @example
|
|
529
|
+
* // Register a SQLite adapter
|
|
530
|
+
* providers: [
|
|
531
|
+
* provideDbAdapter(SqliteAdapter, { database: 'myapp.db' }, 'sqlite')
|
|
532
|
+
* ]
|
|
533
|
+
*
|
|
534
|
+
* // Register with default flavour from adapter
|
|
535
|
+
* providers: [
|
|
536
|
+
* provideDbAdapter(PostgresAdapter, { host: 'localhost', port: 5432 })
|
|
537
|
+
* ]
|
|
538
|
+
*/
|
|
442
539
|
function provideDbAdapter(adapterClass, options = {}, flavour) {
|
|
443
540
|
const adapter = new adapterClass(options);
|
|
444
541
|
if (flavour)
|
|
@@ -451,27 +548,49 @@ function provideDbAdapter(adapterClass, options = {}, flavour) {
|
|
|
451
548
|
useValue: adapter,
|
|
452
549
|
};
|
|
453
550
|
}
|
|
454
|
-
|
|
551
|
+
/**
|
|
552
|
+
* @const {Logger}
|
|
553
|
+
* @private
|
|
554
|
+
* @description Base logger instance for the for-angular module.
|
|
555
|
+
* @memberOf module:lib/for-angular-common.module
|
|
556
|
+
*/
|
|
557
|
+
const log = Logging.for("for-angular");
|
|
558
|
+
/**
|
|
559
|
+
* @description Retrieves a logger instance for the given context.
|
|
560
|
+
* @summary Creates or retrieves a namespaced logger instance using the Decaf logging system.
|
|
561
|
+
* The logger is automatically namespaced under "for-angular" and can be further scoped
|
|
562
|
+
* to a specific instance, function, or string identifier.
|
|
563
|
+
* @param {string | FunctionLike | unknown} instance - The instance, function, or string to scope the logger to
|
|
564
|
+
* @return {Logger} Logger instance for the specified context
|
|
565
|
+
* @memberOf module:lib/for-angular-common.module
|
|
566
|
+
* @example
|
|
567
|
+
* // Get logger for a class
|
|
568
|
+
* const logger = getLogger(MyComponent);
|
|
569
|
+
* logger.info('Component initialized');
|
|
570
|
+
*
|
|
571
|
+
* // Get logger with string identifier
|
|
572
|
+
* const serviceLogger = getLogger('UserService');
|
|
573
|
+
* serviceLogger.error('Operation failed', error);
|
|
574
|
+
*/
|
|
575
|
+
function getLogger(instance) {
|
|
576
|
+
return log.for(instance);
|
|
577
|
+
}
|
|
578
|
+
const CommonModules = [
|
|
455
579
|
CommonModule,
|
|
456
580
|
FormsModule,
|
|
457
581
|
ReactiveFormsModule,
|
|
458
582
|
TranslateModule,
|
|
459
583
|
TranslatePipe
|
|
460
584
|
];
|
|
461
|
-
const log = Logging.for("for-angular");
|
|
462
|
-
function getLogger(instance) {
|
|
463
|
-
return log.for(instance);
|
|
464
|
-
}
|
|
465
585
|
/**
|
|
466
|
-
* @description Main Angular module for the Decaf framework
|
|
586
|
+
* @description Main Angular module for the Decaf framework.
|
|
467
587
|
* @summary The ForAngularCommonModule provides the core functionality for integrating Decaf with Angular applications.
|
|
468
588
|
* It imports and exports common Angular and Ionic components and modules needed for Decaf applications,
|
|
469
589
|
* including form handling, translation support, and Ionic UI components. This module can be imported
|
|
470
590
|
* directly or via the forRoot() method for proper initialization in the application's root module.
|
|
471
|
-
*
|
|
472
591
|
* @class ForAngularCommonModule
|
|
592
|
+
* @memberOf module:lib/for-angular-common.module
|
|
473
593
|
* @example
|
|
474
|
-
* ```typescript
|
|
475
594
|
* // In your app module:
|
|
476
595
|
* @NgModule({
|
|
477
596
|
* imports: [
|
|
@@ -481,17 +600,24 @@ function getLogger(instance) {
|
|
|
481
600
|
* // ...
|
|
482
601
|
* })
|
|
483
602
|
* export class AppModule {}
|
|
484
|
-
* ```
|
|
485
603
|
*/
|
|
486
604
|
class ForAngularCommonModule {
|
|
487
605
|
/**
|
|
488
|
-
* @description Creates a module with providers for root module import
|
|
606
|
+
* @description Creates a module with providers for root module import.
|
|
489
607
|
* @summary This static method provides the proper way to import the ForAngularCommonModule in the application's
|
|
490
608
|
* root module. It returns a ModuleWithProviders object that includes the ForAngularCommonModule itself.
|
|
491
609
|
* Using forRoot() ensures that the module and its providers are properly initialized and only
|
|
492
610
|
* instantiated once in the application.
|
|
493
|
-
*
|
|
494
611
|
* @return {ModuleWithProviders<ForAngularCommonModule>} The module with its providers
|
|
612
|
+
* @memberOf ForAngularCommonModule
|
|
613
|
+
* @static
|
|
614
|
+
* @example
|
|
615
|
+
* // Import in root module
|
|
616
|
+
* @NgModule({
|
|
617
|
+
* imports: [ForAngularCommonModule.forRoot()],
|
|
618
|
+
* // ...
|
|
619
|
+
* })
|
|
620
|
+
* export class AppModule {}
|
|
495
621
|
*/
|
|
496
622
|
static forRoot() {
|
|
497
623
|
return {
|
|
@@ -508,9 +634,7 @@ class ForAngularCommonModule {
|
|
|
508
634
|
ReactiveFormsModule,
|
|
509
635
|
TranslateModule,
|
|
510
636
|
TranslatePipe] }); }
|
|
511
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule,
|
|
512
|
-
{ provide: CPTKN, useValue: '' },
|
|
513
|
-
], imports: [CommonModule,
|
|
637
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule, imports: [CommonModule,
|
|
514
638
|
FormsModule,
|
|
515
639
|
ReactiveFormsModule,
|
|
516
640
|
TranslateModule,
|
|
@@ -522,13 +646,11 @@ class ForAngularCommonModule {
|
|
|
522
646
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule, decorators: [{
|
|
523
647
|
type: NgModule,
|
|
524
648
|
args: [{
|
|
525
|
-
imports:
|
|
649
|
+
imports: CommonModules,
|
|
526
650
|
declarations: [],
|
|
527
|
-
exports:
|
|
651
|
+
exports: CommonModules,
|
|
528
652
|
schemas: [],
|
|
529
|
-
providers: [
|
|
530
|
-
{ provide: CPTKN, useValue: '' },
|
|
531
|
-
],
|
|
653
|
+
providers: [],
|
|
532
654
|
}]
|
|
533
655
|
}] });
|
|
534
656
|
|
|
@@ -1037,15 +1159,15 @@ async function isDarkMode() {
|
|
|
1037
1159
|
*/
|
|
1038
1160
|
|
|
1039
1161
|
/**
|
|
1040
|
-
* @module lib/engine/
|
|
1162
|
+
* @module lib/engine/NgxFormService
|
|
1041
1163
|
* @description Utilities to create and manage Angular forms in Decaf components.
|
|
1042
|
-
* @summary The
|
|
1164
|
+
* @summary The NgxFormService exposes helpers to build FormGroup/FormArray instances
|
|
1043
1165
|
* from component metadata or UI model definitions, register forms in a registry,
|
|
1044
1166
|
* validate and extract form data, and create controls with appropriate validators.
|
|
1045
1167
|
*/
|
|
1046
1168
|
/**
|
|
1047
1169
|
* @description Service for managing Angular forms and form controls.
|
|
1048
|
-
* @summary The
|
|
1170
|
+
* @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.
|
|
1049
1171
|
*
|
|
1050
1172
|
* @class
|
|
1051
1173
|
* @param {WeakMap<AbstractControl, FieldProperties>} controls - A WeakMap to store control properties.
|
|
@@ -1057,17 +1179,17 @@ async function isDarkMode() {
|
|
|
1057
1179
|
* { inputs: { name: 'username', type: 'text', required: true } },
|
|
1058
1180
|
* { inputs: { name: 'password', type: 'password', minLength: 8 } }
|
|
1059
1181
|
* ];
|
|
1060
|
-
* const form =
|
|
1182
|
+
* const form = NgxFormService.createFormFromComponents('loginForm', components, true);
|
|
1061
1183
|
*
|
|
1062
1184
|
* // Validating fields
|
|
1063
|
-
*
|
|
1185
|
+
* NgxFormService.validateFields(form);
|
|
1064
1186
|
*
|
|
1065
1187
|
* // Getting form data
|
|
1066
|
-
* const formData =
|
|
1188
|
+
* const formData = NgxFormService.getFormData(form);
|
|
1067
1189
|
* @mermaid
|
|
1068
1190
|
* sequenceDiagram
|
|
1069
1191
|
* participant C as Component
|
|
1070
|
-
* participant NFS as
|
|
1192
|
+
* participant NFS as NgxFormService
|
|
1071
1193
|
* participant AF as Angular Forms
|
|
1072
1194
|
* C->>NFS: createFormFromComponents()
|
|
1073
1195
|
* NFS->>AF: new FormGroup()
|
|
@@ -1080,7 +1202,7 @@ async function isDarkMode() {
|
|
|
1080
1202
|
* NFS->>AF: Get control values
|
|
1081
1203
|
* NFS-->>C: Return form data
|
|
1082
1204
|
*/
|
|
1083
|
-
class
|
|
1205
|
+
class NgxFormService {
|
|
1084
1206
|
/**
|
|
1085
1207
|
* @description WeakMap that stores control properties for form controls.
|
|
1086
1208
|
* @summary A WeakMap that associates AbstractControl instances with their corresponding FieldProperties.
|
|
@@ -1112,7 +1234,7 @@ class NgxDecafFormService {
|
|
|
1112
1234
|
* @mermaid
|
|
1113
1235
|
* sequenceDiagram
|
|
1114
1236
|
* participant C as Component
|
|
1115
|
-
* participant NFS as
|
|
1237
|
+
* participant NFS as NgxFormService
|
|
1116
1238
|
* participant FR as Form Registry
|
|
1117
1239
|
* participant AF as Angular Forms
|
|
1118
1240
|
* C->>NFS: createForm(id, formArray, registry)
|
|
@@ -1191,7 +1313,7 @@ class NgxDecafFormService {
|
|
|
1191
1313
|
* @private
|
|
1192
1314
|
* @mermaid
|
|
1193
1315
|
* sequenceDiagram
|
|
1194
|
-
* participant NFS as
|
|
1316
|
+
* participant NFS as NgxFormService
|
|
1195
1317
|
* participant FG as FormGroup
|
|
1196
1318
|
* participant FA as FormArray
|
|
1197
1319
|
* NFS->>NFS: Split path into parts
|
|
@@ -1419,8 +1541,8 @@ class NgxDecafFormService {
|
|
|
1419
1541
|
const fullPath = childOf ? isMultiple ? `${childOf}.${index}.${name}` : `${childOf}.${name}` : name;
|
|
1420
1542
|
const [parentGroup, controlName] = this.resolveParentGroup(formGroup, fullPath, componentProps, parentProps);
|
|
1421
1543
|
if (!parentGroup.get(controlName)) {
|
|
1422
|
-
const control =
|
|
1423
|
-
|
|
1544
|
+
const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
|
|
1545
|
+
NgxFormService.register(control, componentProps);
|
|
1424
1546
|
if (parentGroup instanceof FormGroup) {
|
|
1425
1547
|
parentGroup.addControl(controlName, control);
|
|
1426
1548
|
}
|
|
@@ -1452,7 +1574,7 @@ class NgxDecafFormService {
|
|
|
1452
1574
|
* @mermaid
|
|
1453
1575
|
* sequenceDiagram
|
|
1454
1576
|
* participant C as Component
|
|
1455
|
-
* participant NFS as
|
|
1577
|
+
* participant NFS as NgxFormService
|
|
1456
1578
|
* participant FR as Form Registry
|
|
1457
1579
|
* C->>NFS: getControlFromForm(formId, path?)
|
|
1458
1580
|
* NFS->>FR: Get form by formId
|
|
@@ -1497,7 +1619,7 @@ class NgxDecafFormService {
|
|
|
1497
1619
|
* @mermaid
|
|
1498
1620
|
* sequenceDiagram
|
|
1499
1621
|
* participant C as Component
|
|
1500
|
-
* participant NFS as
|
|
1622
|
+
* participant NFS as NgxFormService
|
|
1501
1623
|
* participant AF as Angular Forms
|
|
1502
1624
|
* C->>NFS: createFormFromChildren(id, registry, children)
|
|
1503
1625
|
* NFS->>AF: new FormGroup({})
|
|
@@ -1533,7 +1655,7 @@ class NgxDecafFormService {
|
|
|
1533
1655
|
* @mermaid
|
|
1534
1656
|
* sequenceDiagram
|
|
1535
1657
|
* participant C as Component
|
|
1536
|
-
* participant NFS as
|
|
1658
|
+
* participant NFS as NgxFormService
|
|
1537
1659
|
* participant AF as Angular Forms
|
|
1538
1660
|
* C->>NFS: createFormFromComponents(id, components, registry)
|
|
1539
1661
|
* NFS->>AF: new FormGroup({})
|
|
@@ -1570,7 +1692,7 @@ class NgxDecafFormService {
|
|
|
1570
1692
|
* @mermaid
|
|
1571
1693
|
* sequenceDiagram
|
|
1572
1694
|
* participant C as Component
|
|
1573
|
-
* participant NFS as
|
|
1695
|
+
* participant NFS as NgxFormService
|
|
1574
1696
|
* participant F as Form
|
|
1575
1697
|
* C->>NFS: addControlFromProps(id, componentProps, parentProps?)
|
|
1576
1698
|
* NFS->>NFS: createForm(id, formArray, true)
|
|
@@ -1626,7 +1748,7 @@ class NgxDecafFormService {
|
|
|
1626
1748
|
* @mermaid
|
|
1627
1749
|
* sequenceDiagram
|
|
1628
1750
|
* participant C as Component
|
|
1629
|
-
* participant NFS as
|
|
1751
|
+
* participant NFS as NgxFormService
|
|
1630
1752
|
* participant FG as FormGroup
|
|
1631
1753
|
* participant FC as FormControl
|
|
1632
1754
|
* C->>NFS: getFormData(formGroup)
|
|
@@ -1658,9 +1780,9 @@ class NgxDecafFormService {
|
|
|
1658
1780
|
const data = {};
|
|
1659
1781
|
for (const key in formGroup.controls) {
|
|
1660
1782
|
const control = formGroup.controls[key];
|
|
1661
|
-
const parentProps =
|
|
1783
|
+
const parentProps = NgxFormService.getPropsFromControl(formGroup);
|
|
1662
1784
|
if (!(control instanceof FormControl)) {
|
|
1663
|
-
const value =
|
|
1785
|
+
const value = NgxFormService.getFormData(control);
|
|
1664
1786
|
const isValid = control.valid;
|
|
1665
1787
|
if (parentProps.multiple) {
|
|
1666
1788
|
if (isValid) {
|
|
@@ -1674,7 +1796,7 @@ class NgxDecafFormService {
|
|
|
1674
1796
|
data[key] = value;
|
|
1675
1797
|
continue;
|
|
1676
1798
|
}
|
|
1677
|
-
const props =
|
|
1799
|
+
const props = NgxFormService.getPropsFromControl(control);
|
|
1678
1800
|
let value = control.value;
|
|
1679
1801
|
if (!HTML5CheckTypes.includes(props['type'])) {
|
|
1680
1802
|
switch (props['type']) {
|
|
@@ -1695,7 +1817,7 @@ class NgxDecafFormService {
|
|
|
1695
1817
|
}
|
|
1696
1818
|
data[key] = value;
|
|
1697
1819
|
}
|
|
1698
|
-
|
|
1820
|
+
NgxFormService.enableAllGroupControls(formGroup);
|
|
1699
1821
|
return data;
|
|
1700
1822
|
}
|
|
1701
1823
|
/**
|
|
@@ -1711,7 +1833,7 @@ class NgxDecafFormService {
|
|
|
1711
1833
|
* @mermaid
|
|
1712
1834
|
* sequenceDiagram
|
|
1713
1835
|
* participant C as Component
|
|
1714
|
-
* participant NFS as
|
|
1836
|
+
* participant NFS as NgxFormService
|
|
1715
1837
|
* participant FC as FormControl
|
|
1716
1838
|
* participant FG as FormGroup
|
|
1717
1839
|
* participant FA as FormArray
|
|
@@ -1817,7 +1939,7 @@ class NgxDecafFormService {
|
|
|
1817
1939
|
* @mermaid
|
|
1818
1940
|
* sequenceDiagram
|
|
1819
1941
|
* participant C as Component
|
|
1820
|
-
* participant NFS as
|
|
1942
|
+
* participant NFS as NgxFormService
|
|
1821
1943
|
* participant VF as ValidatorFactory
|
|
1822
1944
|
* participant AF as Angular Forms
|
|
1823
1945
|
* C->>NFS: fromProps(props, updateMode?)
|
|
@@ -1877,7 +1999,7 @@ class NgxDecafFormService {
|
|
|
1877
1999
|
* @mermaid
|
|
1878
2000
|
* sequenceDiagram
|
|
1879
2001
|
* participant C as Component
|
|
1880
|
-
* participant NFS as
|
|
2002
|
+
* participant NFS as NgxFormService
|
|
1881
2003
|
* participant DOM as DOM Tree
|
|
1882
2004
|
* C->>NFS: getParentEl(element, tagName)
|
|
1883
2005
|
* loop Traverse up DOM tree
|
|
@@ -1939,7 +2061,7 @@ class NgxDecafFormService {
|
|
|
1939
2061
|
static reset(formGroup) {
|
|
1940
2062
|
if (formGroup instanceof FormControl) {
|
|
1941
2063
|
const control = formGroup;
|
|
1942
|
-
const { type } =
|
|
2064
|
+
const { type } = NgxFormService.getPropsFromControl(control);
|
|
1943
2065
|
if (!HTML5CheckTypes.includes(type))
|
|
1944
2066
|
control.setValue("");
|
|
1945
2067
|
control.markAsPristine();
|
|
@@ -1950,7 +2072,7 @@ class NgxDecafFormService {
|
|
|
1950
2072
|
else {
|
|
1951
2073
|
for (const key in formGroup.controls) {
|
|
1952
2074
|
const control = formGroup.controls[key];
|
|
1953
|
-
|
|
2075
|
+
NgxFormService.reset(control);
|
|
1954
2076
|
continue;
|
|
1955
2077
|
}
|
|
1956
2078
|
}
|
|
@@ -2120,10 +2242,10 @@ class NgxRenderingEngine extends RenderingEngine {
|
|
|
2120
2242
|
result.instance = NgxRenderingEngine._instance = componentInstance.instance;
|
|
2121
2243
|
}
|
|
2122
2244
|
if (fieldDef.children?.length) {
|
|
2123
|
-
if
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
}
|
|
2245
|
+
// if(!NgxRenderingEngine._parentProps && inputs?.pages) {
|
|
2246
|
+
// NgxRenderingEngine._parentProps = {pages: inputs?.pages};
|
|
2247
|
+
// // NgxRenderingEngine._projectable = false;
|
|
2248
|
+
// }
|
|
2127
2249
|
result.children = fieldDef.children.map((child) => {
|
|
2128
2250
|
// const hiddenOn = (child?.props?.hidden || []) as CrudOperations[];
|
|
2129
2251
|
// moved to ui decorators
|
|
@@ -2135,7 +2257,7 @@ class NgxRenderingEngine extends RenderingEngine {
|
|
|
2135
2257
|
// })
|
|
2136
2258
|
// }
|
|
2137
2259
|
// if(!hiddenOn?.length || !(hiddenOn as CrudOperations[]).includes(operation as CrudOperations))
|
|
2138
|
-
|
|
2260
|
+
NgxFormService.addControlFromProps(registryFormId, child.props, { ...inputs, ...NgxRenderingEngine._parentProps || {} });
|
|
2139
2261
|
return this.fromFieldDefinition(child, vcr, injector, tpl, registryFormId, false, formGroup);
|
|
2140
2262
|
});
|
|
2141
2263
|
}
|
|
@@ -2191,7 +2313,7 @@ class NgxRenderingEngine extends RenderingEngine {
|
|
|
2191
2313
|
*/
|
|
2192
2314
|
static async destroy(formId) {
|
|
2193
2315
|
if (formId)
|
|
2194
|
-
|
|
2316
|
+
NgxFormService.removeRegistry(formId);
|
|
2195
2317
|
NgxRenderingEngine._instance = undefined;
|
|
2196
2318
|
NgxRenderingEngine._parentProps = undefined;
|
|
2197
2319
|
}
|
|
@@ -2235,7 +2357,7 @@ class NgxRenderingEngine extends RenderingEngine {
|
|
|
2235
2357
|
if (!NgxRenderingEngine._operation)
|
|
2236
2358
|
NgxRenderingEngine._operation = props?.operation || undefined;
|
|
2237
2359
|
const isArray = (props?.pages && props?.pages >= 1 || props?.multiple === true);
|
|
2238
|
-
const formGroup =
|
|
2360
|
+
const formGroup = NgxFormService.createForm(formId, isArray);
|
|
2239
2361
|
result = this.fromFieldDefinition(fieldDef, vcr, injector, tpl, formId, true, formGroup);
|
|
2240
2362
|
if (result.instance)
|
|
2241
2363
|
result.instance['formGroup'] = formGroup;
|
|
@@ -2451,6 +2573,24 @@ var errors = {
|
|
|
2451
2573
|
form_control: "Controlador do formulário não encontrado para {0}.",
|
|
2452
2574
|
not_found: "No records found with <span class=\"text-bold\">{0}: {1} in {2}</span>."
|
|
2453
2575
|
};
|
|
2576
|
+
var operations = {
|
|
2577
|
+
read: {
|
|
2578
|
+
success: "Successfully read item with {0} {1}.",
|
|
2579
|
+
error: "Error reading item with {0} {1}."
|
|
2580
|
+
},
|
|
2581
|
+
create: {
|
|
2582
|
+
success: "Successfully created item with {0} {1}.",
|
|
2583
|
+
error: "Error creating item with {0} {1}."
|
|
2584
|
+
},
|
|
2585
|
+
update: {
|
|
2586
|
+
success: "Successfully updated item with {0} {1}.",
|
|
2587
|
+
error: "Error updating item with {0} {1}."
|
|
2588
|
+
},
|
|
2589
|
+
"delete": {
|
|
2590
|
+
success: "Successfully deleted item with {0} {1}.",
|
|
2591
|
+
error: "Error deleting item with {0} {1}."
|
|
2592
|
+
}
|
|
2593
|
+
};
|
|
2454
2594
|
var component = {
|
|
2455
2595
|
fieldset: {
|
|
2456
2596
|
add: "Add another",
|
|
@@ -2512,6 +2652,7 @@ var component = {
|
|
|
2512
2652
|
};
|
|
2513
2653
|
var en = {
|
|
2514
2654
|
errors: errors,
|
|
2655
|
+
operations: operations,
|
|
2515
2656
|
component: component
|
|
2516
2657
|
};
|
|
2517
2658
|
|
|
@@ -2580,10 +2721,17 @@ class I18nLoader {
|
|
|
2580
2721
|
const libKeys = libLanguage[lang] || libLanguage["en"] || {};
|
|
2581
2722
|
const httpRequests$ = forkJoin(this.resources.map(config => this.http.get(`${config.prefix}${lang}${this.getSuffix(config.suffix)}`)));
|
|
2582
2723
|
return httpRequests$.pipe(map(res => {
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2724
|
+
const merged = res.reduce((acc, current) => {
|
|
2725
|
+
for (const key in current) {
|
|
2726
|
+
acc[key] = {
|
|
2727
|
+
...(libKeys[key] || {}),
|
|
2728
|
+
...(acc[key] || {}),
|
|
2729
|
+
...(current[key] || {})
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
return acc;
|
|
2733
|
+
}, {});
|
|
2734
|
+
return merged;
|
|
2587
2735
|
}));
|
|
2588
2736
|
}
|
|
2589
2737
|
}
|
|
@@ -2612,12 +2760,12 @@ function provideI18n(config = { fallbackLang: 'en', lang: 'en' }, resources = []
|
|
|
2612
2760
|
}
|
|
2613
2761
|
|
|
2614
2762
|
/**
|
|
2615
|
-
* @module lib/engine/
|
|
2763
|
+
* @module lib/engine/NgxComponentDirective
|
|
2616
2764
|
* @description Base decaf component abstraction providing shared inputs and utilities.
|
|
2617
|
-
* @summary
|
|
2765
|
+
* @summary NgxComponentDirective is the abstract foundation for Decaf components and provides common
|
|
2618
2766
|
* inputs (model, mapper, pk, props), logging, repository resolution, and event dispatch helpers.
|
|
2619
2767
|
* It centralizes shared behavior for child components and simplifies integration with the rendering engine.
|
|
2620
|
-
* @link {@link
|
|
2768
|
+
* @link {@link NgxComponentDirective}
|
|
2621
2769
|
*/
|
|
2622
2770
|
try {
|
|
2623
2771
|
new NgxRenderingEngine();
|
|
@@ -2633,21 +2781,21 @@ catch (e) {
|
|
|
2633
2781
|
* internationalization support. It implements OnChanges to respond to input property changes
|
|
2634
2782
|
* and includes utilities for navigation, localization, and dynamic property binding. All Decaf
|
|
2635
2783
|
* components should extend this directive to inherit its foundational capabilities.
|
|
2636
|
-
* @class
|
|
2784
|
+
* @class NgxComponentDirective
|
|
2637
2785
|
* @extends {LoggedClass}
|
|
2638
2786
|
* @implements {OnChanges}
|
|
2639
|
-
* @memberOf module:lib/engine/
|
|
2787
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2640
2788
|
*/
|
|
2641
|
-
class
|
|
2789
|
+
class NgxComponentDirective extends LoggedClass {
|
|
2642
2790
|
/**
|
|
2643
|
-
* @description Constructor for
|
|
2791
|
+
* @description Constructor for NgxComponentDirective.
|
|
2644
2792
|
* @summary Initializes the directive by setting up the component name, locale root,
|
|
2645
2793
|
* and logger. Calls the parent LoggedClass constructor and configures localization
|
|
2646
2794
|
* context. The component name and locale root can be optionally injected via the
|
|
2647
2795
|
* CPTKN token, otherwise defaults are used.
|
|
2648
2796
|
* @param {string} [componentName] - Optional component name for identification and logging
|
|
2649
2797
|
* @param {string} [localeRoot] - Optional locale root key for internationalization
|
|
2650
|
-
* @memberOf module:lib/engine/
|
|
2798
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2651
2799
|
*/
|
|
2652
2800
|
// eslint-disable-next-line @angular-eslint/prefer-inject
|
|
2653
2801
|
constructor(componentName, localeRoot) {
|
|
@@ -2659,7 +2807,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2659
2807
|
* By default, it generates a random 16-character value, but it can be explicitly set via input.
|
|
2660
2808
|
* @type {string | number}
|
|
2661
2809
|
* @default generateRandomValue(16)
|
|
2662
|
-
* @memberOf module:lib/engine/
|
|
2810
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2663
2811
|
*/
|
|
2664
2812
|
this.uid = generateRandomValue(16);
|
|
2665
2813
|
/**
|
|
@@ -2669,7 +2817,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2669
2817
|
* provided as a static object mapping or as a function for dynamic mapping transformations.
|
|
2670
2818
|
* @type {Record<string, string> | FunctionLike}
|
|
2671
2819
|
* @default {}
|
|
2672
|
-
* @memberOf module:lib/engine/
|
|
2820
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2673
2821
|
*/
|
|
2674
2822
|
this.mapper = {};
|
|
2675
2823
|
/**
|
|
@@ -2679,7 +2827,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2679
2827
|
* which operation buttons are displayed in the UI. By default, only READ operations are enabled.
|
|
2680
2828
|
* @type {CrudOperations[]}
|
|
2681
2829
|
* @default [OperationKeys.READ]
|
|
2682
|
-
* @memberOf module:lib/engine/
|
|
2830
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2683
2831
|
*/
|
|
2684
2832
|
this.operations = [OperationKeys.READ];
|
|
2685
2833
|
/**
|
|
@@ -2701,7 +2849,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2701
2849
|
* establish the component's vertical placement within the grid structure.
|
|
2702
2850
|
* @type {number}
|
|
2703
2851
|
* @default 1
|
|
2704
|
-
* @memberOf module:lib/engine/
|
|
2852
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2705
2853
|
*/
|
|
2706
2854
|
this.row = 1;
|
|
2707
2855
|
/**
|
|
@@ -2711,7 +2859,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2711
2859
|
* establish the component's horizontal placement within the grid structure.
|
|
2712
2860
|
* @type {number}
|
|
2713
2861
|
* @default 1
|
|
2714
|
-
* @memberOf module:lib/engine/
|
|
2862
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2715
2863
|
*/
|
|
2716
2864
|
this.col = 1;
|
|
2717
2865
|
/**
|
|
@@ -2722,7 +2870,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2722
2870
|
* This provides a way to customize the component's appearance beyond the built-in styling options.
|
|
2723
2871
|
* @type {string}
|
|
2724
2872
|
* @default ""
|
|
2725
|
-
* @memberOf module:lib/engine/
|
|
2873
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2726
2874
|
*/
|
|
2727
2875
|
this.className = '';
|
|
2728
2876
|
/**
|
|
@@ -2733,7 +2881,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2733
2881
|
* and layout features that require menu interaction.
|
|
2734
2882
|
* @protected
|
|
2735
2883
|
* @type {MenuController}
|
|
2736
|
-
* @memberOf module:lib/engine/
|
|
2884
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2737
2885
|
*/
|
|
2738
2886
|
this.menuController = inject(MenuController);
|
|
2739
2887
|
/**
|
|
@@ -2744,7 +2892,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2744
2892
|
* view updates when modifications occur outside the normal Angular change detection flow.
|
|
2745
2893
|
* @protected
|
|
2746
2894
|
* @type {ChangeDetectorRef}
|
|
2747
|
-
* @memberOf module:lib/engine/
|
|
2895
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2748
2896
|
*/
|
|
2749
2897
|
this.changeDetectorRef = inject(ChangeDetectorRef);
|
|
2750
2898
|
/**
|
|
@@ -2754,7 +2902,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2754
2902
|
* including server-side rendering and web workers, without direct DOM access.
|
|
2755
2903
|
* @protected
|
|
2756
2904
|
* @type {Renderer2}
|
|
2757
|
-
* @memberOf module:lib/engine/
|
|
2905
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2758
2906
|
*/
|
|
2759
2907
|
this.renderer = inject(Renderer2);
|
|
2760
2908
|
/**
|
|
@@ -2764,7 +2912,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2764
2912
|
* on the current locale setting, enabling multilingual support throughout the application.
|
|
2765
2913
|
* @protected
|
|
2766
2914
|
* @type {TranslateService}
|
|
2767
|
-
* @memberOf module:lib/engine/
|
|
2915
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2768
2916
|
*/
|
|
2769
2917
|
this.translateService = inject(TranslateService);
|
|
2770
2918
|
/**
|
|
@@ -2774,7 +2922,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2774
2922
|
* state changes. Events are passed up the component hierarchy to enable coordinated
|
|
2775
2923
|
* behavior across the application.
|
|
2776
2924
|
* @type {EventEmitter<IBaseCustomEvent>}
|
|
2777
|
-
* @memberOf module:lib/engine/
|
|
2925
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2778
2926
|
*/
|
|
2779
2927
|
this.listenEvent = new EventEmitter();
|
|
2780
2928
|
/**
|
|
@@ -2784,7 +2932,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2784
2932
|
* handles route parameters, and manages the browser's navigation history.
|
|
2785
2933
|
* @protected
|
|
2786
2934
|
* @type {Router}
|
|
2787
|
-
* @memberOf module:lib/engine/
|
|
2935
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2788
2936
|
*/
|
|
2789
2937
|
this.router = inject(Router);
|
|
2790
2938
|
/**
|
|
@@ -2796,7 +2944,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2796
2944
|
* Additional properties can be included to customize the rendering behavior.
|
|
2797
2945
|
* @type {Record<string, unknown>}
|
|
2798
2946
|
* @default {tag: ""}
|
|
2799
|
-
* @memberOf module:lib/engine/
|
|
2947
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2800
2948
|
*/
|
|
2801
2949
|
this.item = { tag: '' };
|
|
2802
2950
|
/**
|
|
@@ -2808,7 +2956,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2808
2956
|
* enabling customizable component behavior based on external configuration.
|
|
2809
2957
|
* @type {Record<string, unknown>}
|
|
2810
2958
|
* @default {}
|
|
2811
|
-
* @memberOf module:lib/engine/
|
|
2959
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2812
2960
|
*/
|
|
2813
2961
|
this.props = {};
|
|
2814
2962
|
/**
|
|
@@ -2818,7 +2966,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2818
2966
|
* certain operations that require initialization can be performed.
|
|
2819
2967
|
* @type {boolean}
|
|
2820
2968
|
* @default false
|
|
2821
|
-
* @memberOf module:lib/engine/
|
|
2969
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2822
2970
|
*/
|
|
2823
2971
|
this.initialized = false;
|
|
2824
2972
|
/**
|
|
@@ -2829,7 +2977,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2829
2977
|
* while maintaining encapsulation and preventing accidental modification.
|
|
2830
2978
|
* @protected
|
|
2831
2979
|
* @readonly
|
|
2832
|
-
* @memberOf module:lib/engine/
|
|
2980
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2833
2981
|
*/
|
|
2834
2982
|
this.OperationKeys = OperationKeys;
|
|
2835
2983
|
/**
|
|
@@ -2842,7 +2990,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2842
2990
|
* @type {Location}
|
|
2843
2991
|
*/
|
|
2844
2992
|
this.location = inject(Location);
|
|
2845
|
-
this.componentName = componentName || "
|
|
2993
|
+
this.componentName = componentName || "NgxComponentDirective";
|
|
2846
2994
|
this.localeRoot = localeRoot;
|
|
2847
2995
|
if (!this.localeRoot && this.componentName)
|
|
2848
2996
|
this.localeRoot = this.componentName;
|
|
@@ -2855,7 +3003,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2855
3003
|
* @summary Returns the current locale identifier by calling the getLocale method.
|
|
2856
3004
|
* This property provides convenient access to the component's active locale setting.
|
|
2857
3005
|
* @returns {string} The current locale identifier
|
|
2858
|
-
* @memberOf module:lib/engine/
|
|
3006
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2859
3007
|
*/
|
|
2860
3008
|
get localeContext() {
|
|
2861
3009
|
return this.getLocale();
|
|
@@ -2868,7 +3016,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2868
3016
|
* @protected
|
|
2869
3017
|
* @returns {DecafRepository<Model>} The repository instance for the current model
|
|
2870
3018
|
* @throws {InternalError} If repository initialization fails
|
|
2871
|
-
* @memberOf module:lib/engine/
|
|
3019
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2872
3020
|
*/
|
|
2873
3021
|
get repository() {
|
|
2874
3022
|
try {
|
|
@@ -2892,7 +3040,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2892
3040
|
* it updates the component's locale setting accordingly.
|
|
2893
3041
|
* @param {SimpleChanges} changes - Object containing the changed properties with their previous and current values
|
|
2894
3042
|
* @return {void}
|
|
2895
|
-
* @memberOf module:lib/engine/
|
|
3043
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2896
3044
|
*/
|
|
2897
3045
|
ngOnChanges(changes) {
|
|
2898
3046
|
if (changes[BaseComponentProps.MODEL]) {
|
|
@@ -2914,14 +3062,13 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2914
3062
|
* @param {string | string[]} phrase - The translation key or array of keys to translate
|
|
2915
3063
|
* @param {object | string} [params] - Optional parameters for interpolation in translated text
|
|
2916
3064
|
* @return {Promise<string>} A promise that resolves to the translated text
|
|
2917
|
-
* @memberOf module:lib/engine/
|
|
3065
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2918
3066
|
*/
|
|
2919
|
-
translate(phrase, params) {
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
});
|
|
3067
|
+
async translate(phrase, params) {
|
|
3068
|
+
if (typeof params === Primitives.STRING)
|
|
3069
|
+
params = { "0": params };
|
|
3070
|
+
return await firstValueFrom(this.translateService.get(phrase, (params || {})));
|
|
3071
|
+
;
|
|
2925
3072
|
}
|
|
2926
3073
|
/**
|
|
2927
3074
|
* @description Initializes the component asynchronously with custom logic.
|
|
@@ -2932,7 +3079,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2932
3079
|
* @protected
|
|
2933
3080
|
* @param {...unknown[]} args - Variable number of arguments that can be used by child implementations
|
|
2934
3081
|
* @return {Promise<void>} A promise that resolves when initialization is complete
|
|
2935
|
-
* @memberOf module:lib/engine/
|
|
3082
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2936
3083
|
*/
|
|
2937
3084
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2938
3085
|
async initialize(...args) {
|
|
@@ -2946,13 +3093,14 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2946
3093
|
* @protected
|
|
2947
3094
|
* @param {string} [locale] - Optional locale identifier to set
|
|
2948
3095
|
* @return {string} The current locale identifier
|
|
2949
|
-
* @memberOf module:lib/engine/
|
|
3096
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2950
3097
|
*/
|
|
2951
3098
|
getLocale(locale) {
|
|
2952
3099
|
if (locale || !this.locale) {
|
|
2953
3100
|
if (locale)
|
|
2954
3101
|
this.localeRoot = locale;
|
|
2955
|
-
|
|
3102
|
+
if (this.localeRoot)
|
|
3103
|
+
this.locale = getLocaleContext(this.localeRoot);
|
|
2956
3104
|
}
|
|
2957
3105
|
return this.locale;
|
|
2958
3106
|
}
|
|
@@ -2963,7 +3111,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2963
3111
|
* class name using the pattern `/model/{ModelName}`. Returns an empty string if neither
|
|
2964
3112
|
* a route nor a model is available.
|
|
2965
3113
|
* @return {string} The route path for this component
|
|
2966
|
-
* @memberOf module:lib/engine/
|
|
3114
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2967
3115
|
*/
|
|
2968
3116
|
getRoute() {
|
|
2969
3117
|
if (!this.route && this.model instanceof Model)
|
|
@@ -2979,7 +3127,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2979
3127
|
* @template M - The model type extending from Model
|
|
2980
3128
|
* @param {string | Model | ModelConstructor<M>} model - The model to resolve and initialize
|
|
2981
3129
|
* @return {void}
|
|
2982
|
-
* @memberOf module:lib/engine/
|
|
3130
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2983
3131
|
*/
|
|
2984
3132
|
getModel(model) {
|
|
2985
3133
|
if (!(model instanceof Model) && typeof model === Primitives.STRING)
|
|
@@ -2995,7 +3143,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
2995
3143
|
* decorator metadata with the component's runtime configuration.
|
|
2996
3144
|
* @param {Model} model - The model instance to extract definitions from
|
|
2997
3145
|
* @return {void}
|
|
2998
|
-
* @memberOf module:lib/engine/
|
|
3146
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
2999
3147
|
*/
|
|
3000
3148
|
setModelDefinitions(model) {
|
|
3001
3149
|
if (model instanceof Model) {
|
|
@@ -3031,7 +3179,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3031
3179
|
* @mermaid
|
|
3032
3180
|
* sequenceDiagram
|
|
3033
3181
|
* participant C as Component
|
|
3034
|
-
* participant D as
|
|
3182
|
+
* participant D as NgxComponentDirective
|
|
3035
3183
|
* participant P as Props Object
|
|
3036
3184
|
*
|
|
3037
3185
|
* C->>D: parseProps(instance, skip)
|
|
@@ -3049,7 +3197,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3049
3197
|
* end
|
|
3050
3198
|
* end
|
|
3051
3199
|
* @protected
|
|
3052
|
-
* @memberOf module:lib/engine/
|
|
3200
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
3053
3201
|
*/
|
|
3054
3202
|
parseProps(instance, skip = []) {
|
|
3055
3203
|
Object.keys(instance).forEach((key) => {
|
|
@@ -3070,7 +3218,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3070
3218
|
* @param {number} index - The index of the item in the list
|
|
3071
3219
|
* @param {KeyValue | string | number} item - The item data to track
|
|
3072
3220
|
* @return {string | number} A unique identifier for the item
|
|
3073
|
-
* @memberOf module:lib/engine/
|
|
3221
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
3074
3222
|
*/
|
|
3075
3223
|
trackItemFn(index, item) {
|
|
3076
3224
|
return `${index}-${item}`;
|
|
@@ -3087,7 +3235,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3087
3235
|
* @mermaid
|
|
3088
3236
|
* sequenceDiagram
|
|
3089
3237
|
* participant C as Child Component
|
|
3090
|
-
* participant D as
|
|
3238
|
+
* participant D as NgxComponentDirective
|
|
3091
3239
|
* participant H as Event Handler
|
|
3092
3240
|
* participant P as Parent Component
|
|
3093
3241
|
*
|
|
@@ -3107,7 +3255,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3107
3255
|
* else No handlers
|
|
3108
3256
|
* D->>P: listenEvent.emit(event)
|
|
3109
3257
|
* end
|
|
3110
|
-
* @memberOf module:lib/engine/
|
|
3258
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
3111
3259
|
*/
|
|
3112
3260
|
async handleEvent(event) {
|
|
3113
3261
|
let name = "";
|
|
@@ -3145,7 +3293,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3145
3293
|
* @return {boolean} True if the operation is allowed, false otherwise
|
|
3146
3294
|
* @mermaid
|
|
3147
3295
|
* sequenceDiagram
|
|
3148
|
-
* participant D as
|
|
3296
|
+
* participant D as NgxComponentDirective
|
|
3149
3297
|
* participant U as UI
|
|
3150
3298
|
*
|
|
3151
3299
|
* U->>D: isAllowed(operation)
|
|
@@ -3156,7 +3304,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3156
3304
|
* D->>D: Check if operation is not current operation
|
|
3157
3305
|
* D-->>U: Return result
|
|
3158
3306
|
* end
|
|
3159
|
-
* @memberOf module:lib/engine/
|
|
3307
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
3160
3308
|
*/
|
|
3161
3309
|
isAllowed(operation) {
|
|
3162
3310
|
if (!this.operations)
|
|
@@ -3176,7 +3324,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3176
3324
|
* @mermaid
|
|
3177
3325
|
* sequenceDiagram
|
|
3178
3326
|
* participant U as UI
|
|
3179
|
-
* participant D as
|
|
3327
|
+
* participant D as NgxComponentDirective
|
|
3180
3328
|
* participant R as Router
|
|
3181
3329
|
*
|
|
3182
3330
|
* U->>D: Click operation button
|
|
@@ -3187,7 +3335,7 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3187
3335
|
* R->>R: Navigate to new route
|
|
3188
3336
|
* R-->>D: Return navigation result
|
|
3189
3337
|
* D-->>U: Display new operation view
|
|
3190
|
-
* @memberOf module:lib/engine/
|
|
3338
|
+
* @memberOf module:lib/engine/NgxComponentDirective
|
|
3191
3339
|
*/
|
|
3192
3340
|
async changeOperation(operation, id) {
|
|
3193
3341
|
let page = `${this.route}/${operation}/`.replace('//', '/');
|
|
@@ -3197,10 +3345,10 @@ class NgxDecafComponentDirective extends LoggedClass {
|
|
|
3197
3345
|
page = `${page}${this.modelId || id}`;
|
|
3198
3346
|
return this.router.navigateByUrl(page);
|
|
3199
3347
|
}
|
|
3200
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
3201
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type:
|
|
3348
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxComponentDirective, deps: [{ token: CPTKN }, { token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3349
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxComponentDirective, isStandalone: true, inputs: { name: "name", childOf: "childOf", uid: "uid", model: "model", modelId: "modelId", pk: "pk", mapper: "mapper", operations: "operations", operation: "operation", row: "row", col: "col", className: "className", locale: "locale", item: "item", props: "props", route: "route" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
|
|
3202
3350
|
}
|
|
3203
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
3351
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxComponentDirective, decorators: [{
|
|
3204
3352
|
type: Directive,
|
|
3205
3353
|
args: [{ host: { '[attr.id]': 'uid' } }]
|
|
3206
3354
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
@@ -3249,9 +3397,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
3249
3397
|
}] } });
|
|
3250
3398
|
|
|
3251
3399
|
/**
|
|
3252
|
-
* @module lib/engine/
|
|
3400
|
+
* @module lib/engine/NgxFormFieldDirective
|
|
3253
3401
|
* @description Base directive for CRUD form fields in Decaf Angular applications.
|
|
3254
|
-
* @summary Provides the
|
|
3402
|
+
* @summary Provides the NgxFormFieldDirective abstract class that implements ControlValueAccessor
|
|
3255
3403
|
* and FieldProperties to enable form field integration with Angular's reactive forms system.
|
|
3256
3404
|
* This directive handles form control lifecycle, validation, multi-entry forms, and CRUD operations.
|
|
3257
3405
|
*/
|
|
@@ -3263,8 +3411,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
3263
3411
|
* It handles form group lifecycle, error messaging, change detection, and parent-child form relationships.
|
|
3264
3412
|
* Extend this class to create custom form field components that seamlessly integrate with Angular's
|
|
3265
3413
|
* reactive forms and Decaf's validation system.
|
|
3266
|
-
* @class
|
|
3267
|
-
* @extends {
|
|
3414
|
+
* @class NgxFormFieldDirective
|
|
3415
|
+
* @extends {NgxComponentDirective}
|
|
3268
3416
|
* @implements {ControlValueAccessor}
|
|
3269
3417
|
* @implements {FieldProperties}
|
|
3270
3418
|
* @example
|
|
@@ -3278,16 +3426,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
3278
3426
|
* multi: true
|
|
3279
3427
|
* }]
|
|
3280
3428
|
* })
|
|
3281
|
-
* export class TextFieldComponent extends
|
|
3429
|
+
* export class TextFieldComponent extends NgxFormFieldDirective {
|
|
3282
3430
|
* constructor() {
|
|
3283
3431
|
* super();
|
|
3284
3432
|
* }
|
|
3285
3433
|
* }
|
|
3286
3434
|
* ```
|
|
3287
3435
|
*/
|
|
3288
|
-
class
|
|
3289
|
-
|
|
3290
|
-
|
|
3436
|
+
class NgxFormFieldDirective extends NgxComponentDirective {
|
|
3437
|
+
// eslint-disable-next-line @angular-eslint/prefer-inject
|
|
3438
|
+
constructor(componentName = "ComponentCrudField") {
|
|
3439
|
+
super(componentName);
|
|
3291
3440
|
/**
|
|
3292
3441
|
* @description Index of the currently active form group in a form array.
|
|
3293
3442
|
* @summary When working with multiple form groups (form arrays), this indicates
|
|
@@ -3425,12 +3574,12 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
|
|
|
3425
3574
|
case OperationKeys.CREATE:
|
|
3426
3575
|
case OperationKeys.UPDATE:
|
|
3427
3576
|
try {
|
|
3428
|
-
parent =
|
|
3577
|
+
parent = NgxFormService.getParentEl(this.component.nativeElement, 'div');
|
|
3429
3578
|
}
|
|
3430
3579
|
catch (e) {
|
|
3431
3580
|
throw new RenderingError(`Unable to retrieve parent form element for the ${this.operation}: ${e instanceof Error ? e.message : e}`);
|
|
3432
3581
|
}
|
|
3433
|
-
//
|
|
3582
|
+
// NgxFormService.register(parent.id, this.formGroup, this as AngularFieldDefinition);
|
|
3434
3583
|
return parent;
|
|
3435
3584
|
default:
|
|
3436
3585
|
throw new InternalError(`Invalid operation: ${this.operation}`);
|
|
@@ -3468,7 +3617,7 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
|
|
|
3468
3617
|
*/
|
|
3469
3618
|
onDestroy() {
|
|
3470
3619
|
if (this.formGroup)
|
|
3471
|
-
|
|
3620
|
+
NgxFormService.unregister(this.formGroup);
|
|
3472
3621
|
}
|
|
3473
3622
|
/**
|
|
3474
3623
|
* @description Sets the value of the form control.
|
|
@@ -3515,12 +3664,15 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
|
|
|
3515
3664
|
}
|
|
3516
3665
|
}
|
|
3517
3666
|
}
|
|
3518
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
3519
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type:
|
|
3667
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormFieldDirective, deps: [{ token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3668
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormFieldDirective, isStandalone: true, inputs: { activeFormGroupIndex: "activeFormGroupIndex", operation: "operation", parentComponent: "parentComponent", optionsMapper: "optionsMapper" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
|
|
3520
3669
|
}
|
|
3521
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
3670
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormFieldDirective, decorators: [{
|
|
3522
3671
|
type: Directive
|
|
3523
|
-
}], ctorParameters: () => [
|
|
3672
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
3673
|
+
type: Inject,
|
|
3674
|
+
args: [CPTKN]
|
|
3675
|
+
}] }], propDecorators: { activeFormGroupIndex: [{
|
|
3524
3676
|
type: Input
|
|
3525
3677
|
}], operation: [{
|
|
3526
3678
|
type: Input,
|
|
@@ -3545,35 +3697,33 @@ class NgxEventHandler extends LoggedClass {
|
|
|
3545
3697
|
/**
|
|
3546
3698
|
* @module lib/engine/NgxPageDirective
|
|
3547
3699
|
* @description Base page component for Decaf Angular applications.
|
|
3548
|
-
* @summary Provides a page-level base class (NgxPageDirective) that extends
|
|
3700
|
+
* @summary Provides a page-level base class (NgxPageDirective) that extends NgxComponentDirective and
|
|
3549
3701
|
* offers page-focused utilities such as menu management, title handling and router event hooks.
|
|
3550
3702
|
* @link {@link NgxPageDirective}
|
|
3551
3703
|
*/
|
|
3552
3704
|
/**
|
|
3553
3705
|
* @description Base directive for page-level components in Decaf Angular applications.
|
|
3554
3706
|
* @summary Abstract directive that provides foundational functionality for page components.
|
|
3555
|
-
* Extends
|
|
3707
|
+
* Extends NgxComponentDirective to add page-specific features including menu management,
|
|
3556
3708
|
* page title handling, and Ionic lifecycle hooks. This directive serves as the base class for
|
|
3557
3709
|
* all page-level components, providing consistent behavior for navigation, routing, and UI state.
|
|
3558
3710
|
* @class NgxPageDirective
|
|
3559
|
-
* @extends {
|
|
3711
|
+
* @extends {NgxComponentDirective}
|
|
3560
3712
|
* @memberOf module:lib/engine/NgxPageDirective
|
|
3561
3713
|
*/
|
|
3562
|
-
class NgxPageDirective extends
|
|
3714
|
+
class NgxPageDirective extends NgxComponentDirective {
|
|
3563
3715
|
/**
|
|
3564
3716
|
* @description Constructor for NgxPageDirective.
|
|
3565
3717
|
* @summary Initializes the page directive with optional locale root and menu visibility settings.
|
|
3566
|
-
* Calls the parent
|
|
3718
|
+
* Calls the parent NgxComponentDirective constructor to set up base functionality including
|
|
3567
3719
|
* logging, localization, and component identification.
|
|
3568
3720
|
* @param {string} [localeRoot] - Optional locale root key for internationalization
|
|
3569
3721
|
* @param {boolean} [hasMenu=true] - Whether this page should display the menu
|
|
3570
3722
|
* @memberOf module:lib/engine/NgxPageDirective
|
|
3571
3723
|
*/
|
|
3572
3724
|
// eslint-disable-next-line @angular-eslint/prefer-inject
|
|
3573
|
-
constructor(localeRoot, hasMenu = true) {
|
|
3725
|
+
constructor(localeRoot = "NgxPageDirective", hasMenu = true) {
|
|
3574
3726
|
super(localeRoot);
|
|
3575
|
-
this.localeRoot = localeRoot;
|
|
3576
|
-
this.hasMenu = hasMenu;
|
|
3577
3727
|
/**
|
|
3578
3728
|
* @description Page title text for the current view.
|
|
3579
3729
|
* @summary Stores the title text to be displayed for this page. This can be set dynamically
|
|
@@ -3606,6 +3756,19 @@ class NgxPageDirective extends NgxDecafComponentDirective {
|
|
|
3606
3756
|
* @memberOf module:lib/engine/NgxPageDirective
|
|
3607
3757
|
*/
|
|
3608
3758
|
this.titleService = inject(Title);
|
|
3759
|
+
/**
|
|
3760
|
+
* @description Flag indicating whether the page should display the navigation menu.
|
|
3761
|
+
* @summary Controls the visibility and availability of the application menu for this page.
|
|
3762
|
+
* When set to true, the menu is enabled and accessible to users. When false, the menu
|
|
3763
|
+
* is disabled, which is useful for pages like login screens or standalone views that
|
|
3764
|
+
* should not show navigation options.
|
|
3765
|
+
* @protected
|
|
3766
|
+
* @type {boolean}
|
|
3767
|
+
* @default true
|
|
3768
|
+
* @memberOf module:lib/engine/NgxPageDirective
|
|
3769
|
+
*/
|
|
3770
|
+
this.hasMenu = true;
|
|
3771
|
+
this.hasMenu = hasMenu;
|
|
3609
3772
|
}
|
|
3610
3773
|
/**
|
|
3611
3774
|
* @description Ionic lifecycle hook called when the page is about to enter view.
|
|
@@ -3663,1088 +3826,1367 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
3663
3826
|
args: [CPTKN]
|
|
3664
3827
|
}] }] });
|
|
3665
3828
|
|
|
3666
|
-
|
|
3667
|
-
* @module engine
|
|
3668
|
-
* @description Angular rendering engine for Decaf applications
|
|
3669
|
-
* @summary The engine module provides core functionality for rendering Angular components
|
|
3670
|
-
* in Decaf applications. It includes constants, decorators, rendering engines, and utility types
|
|
3671
|
-
* that enable dynamic component creation, property mapping, and component lifecycle management.
|
|
3672
|
-
* Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
|
|
3673
|
-
* for component configuration.
|
|
3674
|
-
*/
|
|
3675
|
-
|
|
3676
|
-
/**
|
|
3677
|
-
* @description Dynamic component renderer for Decaf Angular applications.
|
|
3678
|
-
* @summary This component provides a flexible way to dynamically render Angular components
|
|
3679
|
-
* at runtime based on a tag name. It handles the creation, property binding, and event
|
|
3680
|
-
* subscription for dynamically loaded components. This is particularly useful for
|
|
3681
|
-
* building configurable UIs where components need to be determined at runtime.
|
|
3682
|
-
*
|
|
3683
|
-
* @component {ComponentRendererComponent}
|
|
3684
|
-
* @example
|
|
3685
|
-
* <ngx-decaf-component-renderer
|
|
3686
|
-
* [tag]="tag"
|
|
3687
|
-
* [globals]="globals"
|
|
3688
|
-
* (listenEvent)="listenEvent($event)">
|
|
3689
|
-
* </ngx-decaf-component-renderer>
|
|
3690
|
-
*
|
|
3691
|
-
* @mermaid
|
|
3692
|
-
* classDiagram
|
|
3693
|
-
* class ComponentRendererComponent {
|
|
3694
|
-
* +ViewContainerRef vcr
|
|
3695
|
-
* +string tag
|
|
3696
|
-
* +Record~string, unknown~ globals
|
|
3697
|
-
* +EnvironmentInjector injector
|
|
3698
|
-
* +ComponentRef~unknown~ component
|
|
3699
|
-
* +EventEmitter~IBaseCustomEvent~ listenEvent
|
|
3700
|
-
* +ngOnInit()
|
|
3701
|
-
* +ngOnDestroy()
|
|
3702
|
-
* +ngOnChanges(changes)
|
|
3703
|
-
* -createComponent(tag, globals)
|
|
3704
|
-
* -subscribeEvents()
|
|
3705
|
-
* -unsubscribeEvents()
|
|
3706
|
-
* }
|
|
3707
|
-
* ComponentRendererComponent --|> OnInit
|
|
3708
|
-
* ComponentRendererComponent --|> OnChanges
|
|
3709
|
-
* ComponentRendererComponent --|> OnDestroy
|
|
3710
|
-
*
|
|
3711
|
-
* @implements {OnInit}
|
|
3712
|
-
* @implements {OnChanges}
|
|
3713
|
-
* @implements {OnDestroy}
|
|
3714
|
-
*/
|
|
3715
|
-
class ComponentRendererComponent {
|
|
3716
|
-
/**
|
|
3717
|
-
* @description Creates an instance of ComponentRendererComponent.
|
|
3718
|
-
* @summary Initializes a new ComponentRendererComponent. This component doesn't require
|
|
3719
|
-
* any dependencies to be injected in its constructor as it uses the inject function to
|
|
3720
|
-
* obtain the EnvironmentInjector.
|
|
3721
|
-
*
|
|
3722
|
-
* @memberOf ComponentRendererComponent
|
|
3723
|
-
*/
|
|
3829
|
+
class NgxModelPageDirective extends NgxPageDirective {
|
|
3724
3830
|
constructor() {
|
|
3831
|
+
super(...arguments);
|
|
3725
3832
|
/**
|
|
3726
|
-
* @description
|
|
3727
|
-
* @summary
|
|
3728
|
-
*
|
|
3729
|
-
*
|
|
3730
|
-
*
|
|
3833
|
+
* @description The CRUD operation type to be performed on the model.
|
|
3834
|
+
* @summary Specifies which operation (Create, Read, Update, Delete) this component instance
|
|
3835
|
+
* should perform. This determines the UI behavior, form configuration, and available actions.
|
|
3836
|
+
* The operation affects form validation, field availability, and the specific repository
|
|
3837
|
+
* method called during data submission.
|
|
3731
3838
|
*
|
|
3732
|
-
* @type {
|
|
3733
|
-
* @default
|
|
3734
|
-
*
|
|
3839
|
+
* @type {OperationKeys.CREATE | OperationKeys.READ | OperationKeys.UPDATE | OperationKeys.DELETE}
|
|
3840
|
+
* @default OperationKeys.READ
|
|
3841
|
+
* @memberOf ModelPage
|
|
3735
3842
|
*/
|
|
3736
|
-
this.
|
|
3843
|
+
this.operation = OperationKeys.READ;
|
|
3737
3844
|
/**
|
|
3738
|
-
* @description
|
|
3739
|
-
* @summary
|
|
3740
|
-
*
|
|
3741
|
-
*
|
|
3742
|
-
*
|
|
3845
|
+
* @description Array of operations allowed for the current model instance.
|
|
3846
|
+
* @summary Dynamically determined list of operations that are permitted based on
|
|
3847
|
+
* the current context and model state. Initially contains CREATE and READ operations,
|
|
3848
|
+
* with UPDATE and DELETE added when a modelId is present. This controls which
|
|
3849
|
+
* action buttons are displayed and which operations are accessible to the user.
|
|
3743
3850
|
*
|
|
3744
|
-
* @type {
|
|
3745
|
-
* @
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
this.
|
|
3749
|
-
|
|
3851
|
+
* @type {OperationKeys[]}
|
|
3852
|
+
* @default [OperationKeys.CREATE, OperationKeys.READ]
|
|
3853
|
+
* @memberOf ModelPage
|
|
3854
|
+
*/
|
|
3855
|
+
this.allowedOperations = [OperationKeys.CREATE, OperationKeys.READ];
|
|
3856
|
+
/**
|
|
3857
|
+
* @description Current model data loaded from the repository.
|
|
3858
|
+
* @summary Stores the raw data object representing the current model instance retrieved
|
|
3859
|
+
* from the repository. This property holds the actual data values for the model being
|
|
3860
|
+
* displayed or edited, and is set to undefined when no data is available or when an
|
|
3861
|
+
* error occurs during data loading.
|
|
3862
|
+
* @type {KeyValue | undefined}
|
|
3863
|
+
* @default undefined
|
|
3864
|
+
* @memberOf NgxModelPageDirective
|
|
3865
|
+
*/
|
|
3866
|
+
this.modelData = undefined;
|
|
3750
3867
|
/**
|
|
3751
|
-
* @description
|
|
3752
|
-
* @summary
|
|
3753
|
-
*
|
|
3754
|
-
*
|
|
3755
|
-
*
|
|
3756
|
-
*
|
|
3757
|
-
* @
|
|
3758
|
-
* @memberOf ComponentRendererComponent
|
|
3868
|
+
* @description Error message from failed operations.
|
|
3869
|
+
* @summary Stores error messages that occur during repository operations such as
|
|
3870
|
+
* data loading, creation, update, or deletion. When set, this indicates an error
|
|
3871
|
+
* state that should be displayed to the user. Cleared on successful operations.
|
|
3872
|
+
* @type {string | undefined}
|
|
3873
|
+
* @default undefined
|
|
3874
|
+
* @memberOf NgxModelPageDirective
|
|
3759
3875
|
*/
|
|
3760
|
-
this.
|
|
3761
|
-
this.parent = undefined;
|
|
3762
|
-
this.uid = generateRandomValue(12);
|
|
3763
|
-
this.logger = getLogger(this);
|
|
3876
|
+
this.errorMessage = undefined;
|
|
3764
3877
|
}
|
|
3878
|
+
// constructor(@Inject(CPTKN) hm: boolean = true, @Inject(CPTKN) protected toastController?: ToastController) {
|
|
3879
|
+
// super("NgxModelPageDirective");
|
|
3880
|
+
// }
|
|
3765
3881
|
/**
|
|
3766
|
-
* @description
|
|
3767
|
-
* @summary
|
|
3768
|
-
*
|
|
3769
|
-
*
|
|
3882
|
+
* @description Lazy-initialized repository getter with model resolution.
|
|
3883
|
+
* @summary Creates and returns a repository instance for the specified model name.
|
|
3884
|
+
* Resolves the model constructor from the global registry, instantiates the repository,
|
|
3885
|
+
* and creates a new model instance. Throws an InternalError if the model is not
|
|
3886
|
+
* properly registered with the @Model decorator.
|
|
3770
3887
|
*
|
|
3771
|
-
* @
|
|
3772
|
-
* sequenceDiagram
|
|
3773
|
-
* participant A as Angular Lifecycle
|
|
3774
|
-
* participant C as ComponentRendererComponent
|
|
3775
|
-
* participant R as NgxRenderingEngine
|
|
3888
|
+
* @return {DecafRepository<Model>} The repository instance for the current model
|
|
3776
3889
|
*
|
|
3777
|
-
*
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3890
|
+
* @throws {InternalError} When the model is not found in the registry
|
|
3891
|
+
*/
|
|
3892
|
+
get repository() {
|
|
3893
|
+
if (!this._repository) {
|
|
3894
|
+
const constructor = Model.get(this.modelName);
|
|
3895
|
+
if (!constructor)
|
|
3896
|
+
throw new InternalError('Cannot find model. was it registered with @model?');
|
|
3897
|
+
this._repository = Repository.forModel(constructor);
|
|
3898
|
+
if (!this.pk)
|
|
3899
|
+
this.pk = this._repository.pk;
|
|
3900
|
+
this.model = new constructor();
|
|
3901
|
+
}
|
|
3902
|
+
return this._repository;
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* @description Angular lifecycle hook for component initialization.
|
|
3906
|
+
* @summary Initializes the component by setting up the logger instance using the getLogger
|
|
3907
|
+
* utility. This ensures that logging is available throughout the component's lifecycle
|
|
3908
|
+
* for error tracking and debugging purposes.
|
|
3909
|
+
*/
|
|
3910
|
+
async ionViewWillEnter() {
|
|
3911
|
+
// await super.ionViewWillEnter();
|
|
3912
|
+
if (this.modelId)
|
|
3913
|
+
this.allowedOperations = this.allowedOperations.concat([OperationKeys.UPDATE, OperationKeys.DELETE]);
|
|
3914
|
+
this.getLocale(this.modelName);
|
|
3915
|
+
await this.refresh(this.modelId);
|
|
3916
|
+
this.initialized = true;
|
|
3917
|
+
}
|
|
3918
|
+
/**
|
|
3919
|
+
* @description Refreshes the component data by loading the specified model instance.
|
|
3920
|
+
* @summary Loads model data from the repository based on the current operation type.
|
|
3921
|
+
* For READ, UPDATE, and DELETE operations, fetches the existing model data using
|
|
3922
|
+
* the provided unique identifier. Handles errors gracefully by logging them through
|
|
3923
|
+
* the logger instance.
|
|
3784
3924
|
*
|
|
3785
|
-
* @
|
|
3786
|
-
* @memberOf ComponentRendererComponent
|
|
3925
|
+
* @param {string} [uid] - The unique identifier of the model to load; defaults to modelId
|
|
3787
3926
|
*/
|
|
3788
|
-
|
|
3789
|
-
if (!
|
|
3790
|
-
|
|
3927
|
+
async refresh(uid) {
|
|
3928
|
+
if (!uid)
|
|
3929
|
+
uid = this.modelId;
|
|
3930
|
+
try {
|
|
3931
|
+
this._repository = this.repository;
|
|
3932
|
+
switch (this.operation) {
|
|
3933
|
+
case OperationKeys.READ:
|
|
3934
|
+
case OperationKeys.UPDATE:
|
|
3935
|
+
case OperationKeys.DELETE:
|
|
3936
|
+
this.model = await this.handleGet(uid || this.modelId);
|
|
3937
|
+
break;
|
|
3938
|
+
}
|
|
3791
3939
|
}
|
|
3792
|
-
|
|
3793
|
-
|
|
3940
|
+
catch (error) {
|
|
3941
|
+
if (error instanceof NotFoundError) {
|
|
3942
|
+
this.errorMessage = error.message;
|
|
3943
|
+
}
|
|
3944
|
+
this.logger.error(error);
|
|
3794
3945
|
}
|
|
3795
3946
|
}
|
|
3796
3947
|
/**
|
|
3797
|
-
* @description
|
|
3798
|
-
* @summary
|
|
3799
|
-
*
|
|
3800
|
-
*
|
|
3948
|
+
* @description Generic event handler for component events.
|
|
3949
|
+
* @summary Processes incoming events from child components and routes them to appropriate
|
|
3950
|
+
* handlers based on the event name. Currently handles SUBMIT events by delegating to
|
|
3951
|
+
* the handleSubmit method. This centralized event handling approach allows for easy
|
|
3952
|
+
* extension and consistent event processing.
|
|
3801
3953
|
*
|
|
3802
|
-
* @
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3954
|
+
* @param {IBaseCustomEvent} event - The event object containing event data and metadata
|
|
3955
|
+
*/
|
|
3956
|
+
async handleEvent(event) {
|
|
3957
|
+
const { name } = event;
|
|
3958
|
+
switch (name) {
|
|
3959
|
+
case EventConstants.SUBMIT:
|
|
3960
|
+
await this.handleSubmit(event);
|
|
3961
|
+
break;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
/**
|
|
3965
|
+
* @description Handles form submission events for CRUD operations.
|
|
3966
|
+
* @summary Processes form submission by executing the appropriate repository operation
|
|
3967
|
+
* based on the current operation type. Handles CREATE, UPDATE, and DELETE operations,
|
|
3968
|
+
* processes the form data, refreshes the repository cache, navigates back to the previous
|
|
3969
|
+
* page, and displays success notifications. Comprehensive error handling ensures robust
|
|
3970
|
+
* operation with detailed logging.
|
|
3807
3971
|
*
|
|
3808
|
-
*
|
|
3809
|
-
*
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3972
|
+
* @param {IBaseCustomEvent} event - The submit event containing form data
|
|
3973
|
+
* @return {Promise<IModelPageCustomEvent|void>} Promise that resolves on success or throws on error
|
|
3974
|
+
*/
|
|
3975
|
+
async handleSubmit(event) {
|
|
3976
|
+
try {
|
|
3977
|
+
const repo = this._repository;
|
|
3978
|
+
const data = this.parseData(event.data);
|
|
3979
|
+
const result = this.operation === OperationKeys.CREATE ?
|
|
3980
|
+
await repo.create(data) : this.operation === OperationKeys.UPDATE ?
|
|
3981
|
+
await repo.update(data) : repo.delete(data);
|
|
3982
|
+
const message = await this.translate(`operations.${this.operation}.${result ? 'success' : 'error'}`, {
|
|
3983
|
+
"0": this.pk,
|
|
3984
|
+
"1": this.modelId || result[this.pk],
|
|
3985
|
+
});
|
|
3986
|
+
if (result) {
|
|
3987
|
+
repo.refresh(this.modelName, this.operation, this.modelId);
|
|
3988
|
+
this.location.back();
|
|
3989
|
+
}
|
|
3990
|
+
return {
|
|
3991
|
+
...event,
|
|
3992
|
+
success: result ? true : false,
|
|
3993
|
+
message
|
|
3994
|
+
};
|
|
3995
|
+
}
|
|
3996
|
+
catch (error) {
|
|
3997
|
+
this.logger.error(error);
|
|
3998
|
+
return {
|
|
3999
|
+
...event,
|
|
4000
|
+
success: false,
|
|
4001
|
+
message: error instanceof Error ? error.message : error
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
/**
|
|
4006
|
+
* @description Retrieves a model instance from the repository by unique identifier.
|
|
4007
|
+
* @summary Fetches a specific model instance using the repository's read method.
|
|
4008
|
+
* Handles both string and numeric identifiers by automatically converting numeric
|
|
4009
|
+
* strings to numbers. If no identifier is provided, logs an informational message
|
|
4010
|
+
* and navigates back to the previous page. Returns undefined for missing instances.
|
|
3813
4011
|
*
|
|
3814
|
-
* @
|
|
3815
|
-
* @
|
|
4012
|
+
* @param {string} uid - The unique identifier of the model instance to retrieve
|
|
4013
|
+
* @return {Promise<Model | undefined>} Promise resolving to the model instance or undefined
|
|
3816
4014
|
*/
|
|
3817
|
-
async
|
|
3818
|
-
if (
|
|
3819
|
-
this.
|
|
3820
|
-
|
|
4015
|
+
async handleGet(uid) {
|
|
4016
|
+
if (!uid) {
|
|
4017
|
+
this.logger.info('No key passed to model page read operation, backing to last page');
|
|
4018
|
+
this.location.back();
|
|
4019
|
+
return undefined;
|
|
3821
4020
|
}
|
|
4021
|
+
const type = Reflect.getMetadata("design:type", this.model, this.repository.pk).name;
|
|
4022
|
+
const result = await this._repository.read(([Primitives.NUMBER, Primitives.BIGINT].includes(type.toLowerCase()) ? Number(uid) : uid));
|
|
4023
|
+
return result ?? undefined;
|
|
3822
4024
|
}
|
|
3823
4025
|
/**
|
|
3824
|
-
* @description
|
|
3825
|
-
* @summary
|
|
3826
|
-
*
|
|
3827
|
-
*
|
|
3828
|
-
*
|
|
3829
|
-
* @param {string} tag - The tag name of the component to create
|
|
3830
|
-
* @param {KeyValue} globals - Global properties to pass to the component
|
|
3831
|
-
* @return {void}
|
|
3832
|
-
*
|
|
3833
|
-
* @mermaid
|
|
3834
|
-
* sequenceDiagram
|
|
3835
|
-
* participant C as ComponentRendererComponent
|
|
3836
|
-
* participant R as NgxRenderingEngine
|
|
3837
|
-
* participant V as ViewContainerRef
|
|
3838
|
-
*
|
|
3839
|
-
* C->>R: components(tag)
|
|
3840
|
-
* R-->>C: Return component constructor
|
|
3841
|
-
* C->>C: reflectComponentType(component)
|
|
3842
|
-
* C->>C: Process input properties
|
|
3843
|
-
* C->>C: Filter unmapped properties
|
|
3844
|
-
* C->>V: clear()
|
|
3845
|
-
* C->>R: createComponent(component, props, metadata, vcr, injector, [])
|
|
3846
|
-
* R-->>C: Return component reference
|
|
3847
|
-
* C->>C: subscribeEvents()
|
|
3848
|
-
*
|
|
3849
|
-
* @private
|
|
3850
|
-
* @memberOf ComponentRendererComponent
|
|
3851
|
-
*/
|
|
3852
|
-
createComponent(tag, globals = {}) {
|
|
3853
|
-
const component = NgxRenderingEngine.components(tag)
|
|
3854
|
-
?.constructor;
|
|
3855
|
-
const metadata = reflectComponentType(component);
|
|
3856
|
-
const componentInputs = metadata.inputs;
|
|
3857
|
-
const props = globals?.['item'] || globals?.['props'] || {};
|
|
3858
|
-
if (props?.['tag'])
|
|
3859
|
-
delete props['tag'];
|
|
3860
|
-
if (props?.['children'] && !this.children.length)
|
|
3861
|
-
this.children = props['children'];
|
|
3862
|
-
props['children'] = this.children || [];
|
|
3863
|
-
const inputKeys = Object.keys(props);
|
|
3864
|
-
const unmappedKeys = [];
|
|
3865
|
-
for (const input of inputKeys) {
|
|
3866
|
-
if (!inputKeys.length)
|
|
3867
|
-
break;
|
|
3868
|
-
const prop = componentInputs.find((item) => item.propName === input);
|
|
3869
|
-
if (!prop) {
|
|
3870
|
-
delete props[input];
|
|
3871
|
-
unmappedKeys.push(input);
|
|
3872
|
-
}
|
|
3873
|
-
}
|
|
3874
|
-
const hasChildrenInput = Object.values(componentInputs).some(({ propName }) => propName === AngularEngineKeys.CHILDREN);
|
|
3875
|
-
if (!this.projectable && hasChildrenInput)
|
|
3876
|
-
props[AngularEngineKeys.CHILDREN] = this.children;
|
|
3877
|
-
const hasRootForm = Object.values(componentInputs).some(({ propName }) => propName === BaseComponentProps.PARENT_COMPONENT);
|
|
3878
|
-
if (hasRootForm && this.parentComponent)
|
|
3879
|
-
props[BaseComponentProps.PARENT_COMPONENT] = this.parentComponent;
|
|
3880
|
-
this.vcr.clear();
|
|
3881
|
-
// const projectable = (this.children?.length && this.projectable);
|
|
3882
|
-
// const template = projectable ? this.vcr.createEmbeddedView(this.inner as TemplateRef<unknown>, this.injector).rootNodes : [];
|
|
3883
|
-
this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, []);
|
|
3884
|
-
this.subscribeEvents();
|
|
3885
|
-
}
|
|
3886
|
-
createParentComponent() {
|
|
3887
|
-
const { component, inputs } = this.parent;
|
|
3888
|
-
const metadata = reflectComponentType(component);
|
|
3889
|
-
const template = this.projectable ? this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes : [];
|
|
3890
|
-
this.component = NgxRenderingEngine.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
|
|
3891
|
-
this.subscribeEvents();
|
|
3892
|
-
}
|
|
3893
|
-
/**
|
|
3894
|
-
* @description Subscribes to events emitted by the dynamic component.
|
|
3895
|
-
* @summary This method sets up subscriptions to all EventEmitter properties of the
|
|
3896
|
-
* dynamically created component. When an event is emitted by the dynamic component,
|
|
3897
|
-
* it is captured and re-emitted through the listenEvent output property with additional
|
|
3898
|
-
* metadata about the event source.
|
|
3899
|
-
*
|
|
3900
|
-
* @mermaid
|
|
3901
|
-
* sequenceDiagram
|
|
3902
|
-
* participant C as ComponentRendererComponent
|
|
3903
|
-
* participant D as Dynamic Component
|
|
3904
|
-
* participant P as Parent Component
|
|
3905
|
-
*
|
|
3906
|
-
* C->>C: subscribeEvents()
|
|
3907
|
-
* C->>D: Get instance properties
|
|
3908
|
-
* loop For each property
|
|
3909
|
-
* C->>C: Check if property is EventEmitter
|
|
3910
|
-
* alt is EventEmitter
|
|
3911
|
-
* C->>D: Subscribe to event
|
|
3912
|
-
* D-->>C: Event emitted
|
|
3913
|
-
* C->>P: Re-emit event with metadata
|
|
3914
|
-
* end
|
|
3915
|
-
* end
|
|
3916
|
-
*
|
|
3917
|
-
* @private
|
|
3918
|
-
* @return {void}
|
|
3919
|
-
* @memberOf ComponentRendererComponent
|
|
3920
|
-
*/
|
|
3921
|
-
subscribeEvents() {
|
|
3922
|
-
if (this.component) {
|
|
3923
|
-
const instance = this.component?.instance;
|
|
3924
|
-
const componentKeys = Object.keys(instance);
|
|
3925
|
-
for (const key of componentKeys) {
|
|
3926
|
-
const value = instance[key];
|
|
3927
|
-
if (value instanceof EventEmitter)
|
|
3928
|
-
instance[key].subscribe((event) => {
|
|
3929
|
-
this.listenEvent.emit({
|
|
3930
|
-
name: key,
|
|
3931
|
-
...event,
|
|
3932
|
-
});
|
|
3933
|
-
});
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3936
|
-
}
|
|
3937
|
-
/**
|
|
3938
|
-
* @description Unsubscribes from all events of the dynamic component.
|
|
3939
|
-
* @summary This method cleans up event subscriptions when the component is being destroyed.
|
|
3940
|
-
* It iterates through all properties of the dynamic component instance and unsubscribes
|
|
3941
|
-
* from any EventEmitter properties to prevent memory leaks and unexpected behavior after
|
|
3942
|
-
* the component is destroyed.
|
|
3943
|
-
*
|
|
3944
|
-
* @mermaid
|
|
3945
|
-
* sequenceDiagram
|
|
3946
|
-
* participant C as ComponentRendererComponent
|
|
3947
|
-
* participant D as Dynamic Component
|
|
3948
|
-
*
|
|
3949
|
-
* C->>C: unsubscribeEvents()
|
|
3950
|
-
* C->>D: Get instance properties
|
|
3951
|
-
* loop For each property
|
|
3952
|
-
* C->>C: Check if property is EventEmitter
|
|
3953
|
-
* alt is EventEmitter
|
|
3954
|
-
* C->>D: Unsubscribe from event
|
|
3955
|
-
* end
|
|
3956
|
-
* end
|
|
4026
|
+
* @description Parses and transforms form data for repository operations.
|
|
4027
|
+
* @summary Converts raw form data into the appropriate format for repository operations.
|
|
4028
|
+
* For DELETE operations, returns the primary key value (string or number). For CREATE
|
|
4029
|
+
* and UPDATE operations, builds a complete model instance using the Model.build method
|
|
4030
|
+
* with proper primary key assignment for updates.
|
|
3957
4031
|
*
|
|
4032
|
+
* @param {Partial<Model>} data - The raw form data to be processed
|
|
4033
|
+
* @return {Model | string | number} Processed data ready for repository operations
|
|
3958
4034
|
* @private
|
|
3959
|
-
* @return {void}
|
|
3960
|
-
* @memberOf ComponentRendererComponent
|
|
3961
4035
|
*/
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
}
|
|
3973
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3974
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", children: "children", projectable: "projectable", model: "model", parentComponent: "parentComponent", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, 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: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<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 @if(projectable) {\n @if(children?.length) {\n @for(child of 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 <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
4036
|
+
parseData(data) {
|
|
4037
|
+
const repo = this._repository;
|
|
4038
|
+
let uid = this.modelId;
|
|
4039
|
+
if (repo.pk === 'id')
|
|
4040
|
+
uid = Number(uid);
|
|
4041
|
+
if (this.operation !== OperationKeys.DELETE)
|
|
4042
|
+
return Model.build(this.modelId ? Object.assign(data, { [repo.pk]: uid }) : data, this.modelName);
|
|
4043
|
+
return uid;
|
|
4044
|
+
}
|
|
4045
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxModelPageDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4046
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxModelPageDirective, isStandalone: true, inputs: { modelId: "modelId", operation: "operation", modelName: "modelName" }, usesInheritance: true, ngImport: i0 }); }
|
|
3975
4047
|
}
|
|
3976
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
3977
|
-
type:
|
|
3978
|
-
|
|
3979
|
-
}], ctorParameters: () => [], propDecorators: { vcr: [{
|
|
3980
|
-
type: ViewChild,
|
|
3981
|
-
args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
|
|
3982
|
-
}], tag: [{
|
|
3983
|
-
type: Input,
|
|
3984
|
-
args: [{ required: true }]
|
|
3985
|
-
}], globals: [{
|
|
3986
|
-
type: Input
|
|
3987
|
-
}], children: [{
|
|
3988
|
-
type: Input
|
|
3989
|
-
}], projectable: [{
|
|
3990
|
-
type: Input
|
|
3991
|
-
}], listenEvent: [{
|
|
3992
|
-
type: Output
|
|
3993
|
-
}], model: [{
|
|
4048
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxModelPageDirective, decorators: [{
|
|
4049
|
+
type: Directive
|
|
4050
|
+
}], propDecorators: { modelId: [{
|
|
3994
4051
|
type: Input
|
|
3995
|
-
}],
|
|
4052
|
+
}], operation: [{
|
|
3996
4053
|
type: Input
|
|
3997
|
-
}],
|
|
4054
|
+
}], modelName: [{
|
|
3998
4055
|
type: Input
|
|
3999
|
-
}], inner: [{
|
|
4000
|
-
type: ViewChild,
|
|
4001
|
-
args: ['inner', { read: TemplateRef, static: true }]
|
|
4002
4056
|
}] } });
|
|
4003
4057
|
|
|
4004
4058
|
/**
|
|
4005
|
-
* @
|
|
4006
|
-
* @
|
|
4007
|
-
*
|
|
4008
|
-
* and
|
|
4009
|
-
* supports various input types (text, number, date, select, etc.), validation rules, and styling
|
|
4010
|
-
* options, making it suitable for building dynamic forms for create, read, update, and delete
|
|
4011
|
-
* operations.
|
|
4012
|
-
*
|
|
4013
|
-
* @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
|
|
4014
|
-
* @param {string} name - The field name, used as form control identifier
|
|
4015
|
-
* @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
|
|
4016
|
-
* @param {string|number|Date} value - The initial value of the field
|
|
4017
|
-
* @param {boolean} disabled - Whether the field is disabled
|
|
4018
|
-
* @param {string} label - The display label for the field
|
|
4019
|
-
* @param {string} placeholder - Placeholder text when field is empty
|
|
4020
|
-
* @param {string} format - Format pattern for the field value
|
|
4021
|
-
* @param {boolean} hidden - Whether the field should be hidden
|
|
4022
|
-
* @param {number|Date} max - Maximum allowed value
|
|
4023
|
-
* @param {number} maxlength - Maximum allowed length
|
|
4024
|
-
* @param {number|Date} min - Minimum allowed value
|
|
4025
|
-
* @param {number} minlength - Minimum allowed length
|
|
4026
|
-
* @param {string} pattern - Validation pattern
|
|
4027
|
-
* @param {boolean} readonly - Whether the field is read-only
|
|
4028
|
-
* @param {boolean} required - Whether the field is required
|
|
4029
|
-
* @param {number} step - Step value for number inputs
|
|
4030
|
-
* @param {FormGroup} formGroup - The parent form group
|
|
4031
|
-
* @param {StringOrBoolean} translatable - Whether field labels should be translated
|
|
4032
|
-
*
|
|
4033
|
-
* @component CrudFieldComponent
|
|
4034
|
-
* @example
|
|
4035
|
-
* <ngx-decaf-crud-field
|
|
4036
|
-
* operation="create"
|
|
4037
|
-
* name="firstName"
|
|
4038
|
-
* type="text"
|
|
4039
|
-
* label="<NAME>"
|
|
4040
|
-
* placeholder="<NAME>"
|
|
4041
|
-
* [value]="model.firstName"
|
|
4042
|
-
* [disabled]="model.readOnly">
|
|
4059
|
+
* @module module:lib/engine/NgxParentComponentDirective
|
|
4060
|
+
* @description Directive base for parent container components used by the rendering system.
|
|
4061
|
+
* @summary Provides NgxParentComponentDirective which offers inputs for children metadata,
|
|
4062
|
+
* column/row configuration and parent component wiring used by layout and container components.
|
|
4043
4063
|
*
|
|
4064
|
+
* @link {@link NgxParentComponentDirective}
|
|
4065
|
+
*/
|
|
4066
|
+
/**
|
|
4067
|
+
* @description Layout component for creating responsive grid layouts in Angular applications.
|
|
4068
|
+
* @summary This component provides a flexible grid system that can be configured with dynamic
|
|
4069
|
+
* rows and columns. It supports responsive breakpoints and can render child components within
|
|
4070
|
+
* the grid structure. The component extends NgxComponentDirective to inherit common functionality
|
|
4071
|
+
* and integrates with the model and component renderer systems.
|
|
4044
4072
|
*
|
|
4045
|
-
* @
|
|
4073
|
+
* @class NgxParentComponentDirective
|
|
4074
|
+
* @extends {NgxParentComponentDirective}
|
|
4075
|
+
* @implements {OnInit}
|
|
4046
4076
|
*/
|
|
4047
|
-
|
|
4077
|
+
class NgxParentComponentDirective extends NgxComponentDirective {
|
|
4048
4078
|
constructor() {
|
|
4049
4079
|
super(...arguments);
|
|
4050
|
-
this.className = 'dcf-width-1-1';
|
|
4051
4080
|
/**
|
|
4052
|
-
* @description
|
|
4053
|
-
* @summary
|
|
4081
|
+
* @description Array of UI model metadata for all form fields.
|
|
4082
|
+
* @summary Contains the complete collection of UI model metadata that defines
|
|
4083
|
+
* the structure, validation, and presentation of form fields across all pages.
|
|
4084
|
+
* Each metadata object contains information about field type, validation rules,
|
|
4085
|
+
* page assignment, and display properties.
|
|
4054
4086
|
*
|
|
4055
|
-
* @type {
|
|
4056
|
-
* @memberOf CrudFieldComponent
|
|
4087
|
+
* @type {UIModelMetadata[]}
|
|
4057
4088
|
*/
|
|
4089
|
+
this.children = [];
|
|
4058
4090
|
/**
|
|
4059
|
-
* @description
|
|
4060
|
-
* @summary
|
|
4061
|
-
*
|
|
4062
|
-
*
|
|
4091
|
+
* @description Number of columns or array of column definitions for the grid layout.
|
|
4092
|
+
* @summary Defines the column structure of the grid. When a number is provided, it creates
|
|
4093
|
+
* that many equal-width columns. When an array is provided, each element can define specific
|
|
4094
|
+
* column properties or sizing. This allows for flexible grid layouts that can adapt to
|
|
4095
|
+
* different content requirements.
|
|
4063
4096
|
*
|
|
4064
|
-
* @type {string}
|
|
4065
|
-
* @default
|
|
4066
|
-
* @memberOf CrudFieldComponent
|
|
4097
|
+
* @type {(number | string[])}
|
|
4098
|
+
* @default 1
|
|
4067
4099
|
*/
|
|
4068
|
-
this.
|
|
4100
|
+
this.cols = 1;
|
|
4069
4101
|
/**
|
|
4070
|
-
* @description
|
|
4071
|
-
* @summary
|
|
4072
|
-
*
|
|
4073
|
-
*
|
|
4074
|
-
* @type {string | number | Date}
|
|
4075
|
-
* @default ''
|
|
4076
|
-
* @memberOf CrudFieldComponent
|
|
4077
|
-
*/
|
|
4078
|
-
this.value = '';
|
|
4079
|
-
/**
|
|
4080
|
-
* @description Whether the field should be hidden.
|
|
4081
|
-
* @summary When true, the field will not be visible in the UI but will still be part of the form model.
|
|
4082
|
-
* This is useful for fields that need to be included in form submission but should not be displayed to the user.
|
|
4083
|
-
*
|
|
4084
|
-
* @type {boolean}
|
|
4085
|
-
* @memberOf CrudFieldComponent
|
|
4086
|
-
*/
|
|
4087
|
-
this.hidden = false;
|
|
4088
|
-
/**
|
|
4089
|
-
* @description Interface style for select inputs.
|
|
4090
|
-
* @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
|
|
4091
|
-
* This determines how the select options are presented to the user.
|
|
4092
|
-
*
|
|
4093
|
-
* @type {SelectInterface}
|
|
4094
|
-
* @memberOf CrudFieldComponent
|
|
4095
|
-
*/
|
|
4096
|
-
this.interface = 'popover';
|
|
4097
|
-
/**
|
|
4098
|
-
* @description Spellcheck attribute for text inputs.
|
|
4099
|
-
* @summary Enables or disables spellchecking for text inputs.
|
|
4100
|
-
* When true, the browser will check the spelling of the input text.
|
|
4101
|
-
*
|
|
4102
|
-
* @type {boolean}
|
|
4103
|
-
* @default false
|
|
4104
|
-
* @memberOf CrudFieldComponent
|
|
4105
|
-
*/
|
|
4106
|
-
this.spellcheck = false;
|
|
4107
|
-
/**
|
|
4108
|
-
* @description Input mode for text inputs.
|
|
4109
|
-
* @summary Hints at the type of data that might be entered by the user while editing the element.
|
|
4110
|
-
* This can affect the virtual keyboard layout on mobile devices.
|
|
4102
|
+
* @description Number of rows or array of row definitions for the grid layout.
|
|
4103
|
+
* @summary Defines the row structure of the grid. When a number is provided, it creates
|
|
4104
|
+
* that many equal-height rows. When an array is provided, each element can define specific
|
|
4105
|
+
* row properties or sizing. This provides control over vertical spacing and content organization.
|
|
4111
4106
|
*
|
|
4112
|
-
* @type {
|
|
4113
|
-
* @default
|
|
4114
|
-
* @memberOf CrudFieldComponent
|
|
4107
|
+
* @type {(number | string[])}
|
|
4108
|
+
* @default 1
|
|
4115
4109
|
*/
|
|
4116
|
-
this.
|
|
4110
|
+
this.rows = 1;
|
|
4111
|
+
}
|
|
4112
|
+
async ngOnInit(model) {
|
|
4113
|
+
if (model)
|
|
4114
|
+
this.model = model;
|
|
4115
|
+
if (this.model && !this.repository)
|
|
4116
|
+
this._repository = this.repository;
|
|
4117
|
+
}
|
|
4118
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4119
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxParentComponentDirective, isStandalone: true, inputs: { parentComponent: "parentComponent", children: "children", cols: "cols", rows: "rows" }, usesInheritance: true, ngImport: i0 }); }
|
|
4120
|
+
}
|
|
4121
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, decorators: [{
|
|
4122
|
+
type: Directive
|
|
4123
|
+
}], propDecorators: { parentComponent: [{
|
|
4124
|
+
type: Input
|
|
4125
|
+
}], children: [{
|
|
4126
|
+
type: Input
|
|
4127
|
+
}], cols: [{
|
|
4128
|
+
type: Input
|
|
4129
|
+
}], rows: [{
|
|
4130
|
+
type: Input
|
|
4131
|
+
}] } });
|
|
4132
|
+
|
|
4133
|
+
class NgxFormDirective extends NgxParentComponentDirective {
|
|
4134
|
+
constructor() {
|
|
4135
|
+
super(...arguments);
|
|
4136
|
+
this.crudFieldComponent = ComponentsTagNames.CRUD_FIELD;
|
|
4117
4137
|
/**
|
|
4118
|
-
* @description
|
|
4119
|
-
* @summary
|
|
4120
|
-
* This
|
|
4138
|
+
* @description Field update trigger mode for form validation.
|
|
4139
|
+
* @summary Determines when form field validation should be triggered. Options include
|
|
4140
|
+
* 'change', 'blur', or 'submit'. This affects the user experience by controlling
|
|
4141
|
+
* when validation feedback is shown to the user during form interaction.
|
|
4121
4142
|
*
|
|
4122
|
-
* @type {
|
|
4123
|
-
* @default '
|
|
4124
|
-
* @memberOf CrudFieldComponent
|
|
4143
|
+
* @type {FieldUpdateMode}
|
|
4144
|
+
* @default 'change'
|
|
4125
4145
|
*/
|
|
4126
|
-
this.
|
|
4146
|
+
this.updateOn = 'change';
|
|
4127
4147
|
/**
|
|
4128
|
-
* @description
|
|
4129
|
-
* @summary
|
|
4130
|
-
*
|
|
4148
|
+
* @description Form submission target specification.
|
|
4149
|
+
* @summary Specifies where to display the response after form submission, similar
|
|
4150
|
+
* to the HTML form target attribute. Options include '_self', '_blank', '_parent',
|
|
4151
|
+
* '_top', or a named frame. Controls the browser behavior for form responses.
|
|
4131
4152
|
*
|
|
4132
|
-
* @type {
|
|
4133
|
-
* @default '
|
|
4134
|
-
* @memberOf CrudFieldComponent
|
|
4153
|
+
* @type {HTMLFormTarget}
|
|
4154
|
+
* @default '_self'
|
|
4135
4155
|
*/
|
|
4136
|
-
this.
|
|
4156
|
+
this.target = '_self';
|
|
4137
4157
|
/**
|
|
4138
|
-
* @description
|
|
4139
|
-
* @summary
|
|
4140
|
-
*
|
|
4158
|
+
* @description HTTP method or submission strategy for the form.
|
|
4159
|
+
* @summary Defines how the form should be submitted. 'get' and 'post' correspond
|
|
4160
|
+
* to standard HTTP methods for traditional form submission, while 'event' uses
|
|
4161
|
+
* Angular event-driven submission for single-page application workflows.
|
|
4141
4162
|
*
|
|
4142
|
-
* @type {'
|
|
4143
|
-
* @default '
|
|
4144
|
-
* @memberOf CrudFieldComponent
|
|
4163
|
+
* @type {'get' | 'post' | 'event'}
|
|
4164
|
+
* @default 'event'
|
|
4145
4165
|
*/
|
|
4146
|
-
this.
|
|
4166
|
+
this.method = 'event';
|
|
4147
4167
|
/**
|
|
4148
|
-
* @description
|
|
4149
|
-
* @summary
|
|
4150
|
-
*
|
|
4168
|
+
* @description The current CRUD operation being performed.
|
|
4169
|
+
* @summary Specifies the type of operation this form is handling (CREATE, READ, UPDATE, DELETE).
|
|
4170
|
+
* This is a required input that determines form behavior, validation rules, and available actions.
|
|
4171
|
+
* The operation affects form state, button visibility, and submission logic.
|
|
4151
4172
|
*
|
|
4152
|
-
* @type {
|
|
4153
|
-
* @
|
|
4154
|
-
* @memberOf CrudFieldComponent
|
|
4173
|
+
* @type {CrudOperations}
|
|
4174
|
+
* @required
|
|
4155
4175
|
*/
|
|
4156
|
-
this.
|
|
4176
|
+
this.operation = OperationKeys.CREATE;
|
|
4157
4177
|
/**
|
|
4158
|
-
* @description
|
|
4159
|
-
* @summary
|
|
4160
|
-
*
|
|
4161
|
-
*
|
|
4178
|
+
* @description Angular reactive FormGroup for form state management.
|
|
4179
|
+
* @summary The FormGroup instance that manages all form controls, validation,
|
|
4180
|
+
* and form state. This is the main interface for accessing form values and
|
|
4181
|
+
* controlling form behavior. May be undefined for read-only operations.
|
|
4162
4182
|
*
|
|
4163
|
-
* @type {
|
|
4164
|
-
* @default false
|
|
4165
|
-
* @memberOf CrudFieldComponent
|
|
4183
|
+
* @type {FormGroup | undefined}
|
|
4166
4184
|
*/
|
|
4167
|
-
this.
|
|
4185
|
+
this.formGroup = undefined;
|
|
4168
4186
|
/**
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4187
|
+
* @description Event emitter for form submission events.
|
|
4188
|
+
* @summary Emits ICrudFormEvent objects when the form is submitted, providing
|
|
4189
|
+
* form data, component information, and any associated handlers to parent
|
|
4190
|
+
* components. This enables decoupled handling of form submission logic.
|
|
4191
|
+
*
|
|
4192
|
+
* @type {EventEmitter<ICrudFormEvent>}
|
|
4193
|
+
*/
|
|
4194
|
+
this.submitEvent = new EventEmitter();
|
|
4176
4195
|
/**
|
|
4177
|
-
* @description
|
|
4178
|
-
* @summary
|
|
4179
|
-
*
|
|
4196
|
+
* @description Unique identifier for the current record instance.
|
|
4197
|
+
* @summary This property holds a unique string value that identifies the specific record being managed by the form.
|
|
4198
|
+
* It is automatically generated if not provided, ensuring each form instance has a distinct identifier.
|
|
4199
|
+
* The uid is used for tracking, referencing, and emitting events related to the current record, and may be used
|
|
4200
|
+
* in conjunction with the primary key for CRUD operations.
|
|
4180
4201
|
*
|
|
4181
|
-
* @type {
|
|
4182
|
-
* @default
|
|
4183
|
-
* @memberOf CrudFieldComponent
|
|
4202
|
+
* @type {string}
|
|
4203
|
+
* @default Randomly generated 12-character string
|
|
4184
4204
|
*/
|
|
4185
|
-
this.
|
|
4205
|
+
this.allowClear = true;
|
|
4206
|
+
// /**
|
|
4207
|
+
// * @description Angular change detection service.
|
|
4208
|
+
// * @summary Injected service that provides manual control over change detection cycles.
|
|
4209
|
+
// * This is essential for ensuring that programmatic DOM changes (like setting accordion
|
|
4210
|
+
// * attributes) are properly reflected in the component's state and trigger appropriate
|
|
4211
|
+
// * view updates when modifications occur outside the normal Angular change detection flow.
|
|
4212
|
+
// *
|
|
4213
|
+
// * @protected
|
|
4214
|
+
// * @type {ChangeDetectorRef}
|
|
4215
|
+
// * @memberOf CrudFormComponent
|
|
4216
|
+
// */
|
|
4217
|
+
// protected changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
|
|
4218
|
+
// /**
|
|
4219
|
+
// * @description Angular Renderer2 service for safe DOM manipulation.
|
|
4220
|
+
// * @summary Injected service that provides a safe, platform-agnostic way to manipulate DOM elements.
|
|
4221
|
+
// * This service ensures proper handling of DOM operations across different platforms and environments,
|
|
4222
|
+
// * including server-side rendering and web workers.
|
|
4223
|
+
// *
|
|
4224
|
+
// * @protected
|
|
4225
|
+
// * @type {Renderer2}
|
|
4226
|
+
// * @memberOf CrudFormComponent
|
|
4227
|
+
// */
|
|
4228
|
+
// protected renderer: Renderer2 = inject(Renderer2);
|
|
4229
|
+
// /**
|
|
4230
|
+
// * @description Translation service for internationalization.
|
|
4231
|
+
// * @summary Injected service that provides translation capabilities for UI text.
|
|
4232
|
+
// * Used to translate button labels and validation messages based on the current locale.
|
|
4233
|
+
// *
|
|
4234
|
+
// * @protected
|
|
4235
|
+
// * @type {TranslateService}
|
|
4236
|
+
// * @memberOf CrudFormComponent
|
|
4237
|
+
// */
|
|
4238
|
+
// protected translateService: TranslateService = inject(TranslateService);
|
|
4239
|
+
this.activeFormGroupIndex = 0;
|
|
4186
4240
|
}
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
* @summary Initializes the field component based on the operation type and field configuration.
|
|
4190
|
-
* For READ and DELETE operations, removes the form group to make fields read-only.
|
|
4191
|
-
* For other operations, sets up icons, configures multi-value support if needed,
|
|
4192
|
-
* and sets default values for radio buttons if no value is provided.
|
|
4193
|
-
*
|
|
4194
|
-
* @returns {void}
|
|
4195
|
-
* @memberOf CrudFieldComponent
|
|
4196
|
-
*/
|
|
4197
|
-
async ngOnInit() {
|
|
4198
|
-
this.options = await this.getOptions();
|
|
4199
|
-
addIcons({ chevronDownOutline, chevronUpOutline });
|
|
4200
|
-
if (Array.isArray(this.hidden) && !this.hidden.includes(this.operation)) {
|
|
4201
|
-
this.hidden = false;
|
|
4202
|
-
}
|
|
4203
|
-
if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
|
|
4204
|
-
this.formGroup = undefined;
|
|
4205
|
-
}
|
|
4206
|
-
else {
|
|
4207
|
-
if (!this.parentComponent && this.formGroup instanceof FormGroup || this.formGroup instanceof FormArray)
|
|
4208
|
-
this.parentComponent = (this.formGroup.root || this.formControl.root);
|
|
4209
|
-
if (this.multiple) {
|
|
4210
|
-
this.formGroup = this.activeFormGroup;
|
|
4211
|
-
if (!this.parentComponent)
|
|
4212
|
-
this.parentComponent = this.formGroup.parent;
|
|
4213
|
-
this.formControl = this.formGroup.get(this.name);
|
|
4214
|
-
}
|
|
4215
|
-
if (this.type === HTML5InputTypes.CHECKBOX && Array.isArray(this.value)) {
|
|
4216
|
-
this.setValue(this.value);
|
|
4217
|
-
}
|
|
4218
|
-
}
|
|
4241
|
+
get activeFormGroup() {
|
|
4242
|
+
return this.getFormArrayIndex(this.activeFormGroupIndex);
|
|
4219
4243
|
}
|
|
4220
4244
|
/**
|
|
4221
|
-
*
|
|
4222
|
-
*
|
|
4223
|
-
*
|
|
4245
|
+
* @description Component initialization lifecycle method.
|
|
4246
|
+
* @summary Initializes the component by setting up the logger, configuring form state
|
|
4247
|
+
* based on the operation type, and merging configuration options. For READ and DELETE
|
|
4248
|
+
* operations, the formGroup is set to undefined since these operations don't require
|
|
4249
|
+
* form input. Configuration options are merged with default settings.
|
|
4224
4250
|
*
|
|
4225
|
-
* @returns {
|
|
4226
|
-
* @memberOf CrudFieldComponent
|
|
4251
|
+
* @returns {Promise<void>}
|
|
4227
4252
|
*/
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
if (this.
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
}
|
|
4235
|
-
if (this.optionsMapper) {
|
|
4236
|
-
if (this.optionsMapper instanceof Function || typeof this.optionsMapper === 'function') {
|
|
4237
|
-
const mapper = this.optionsMapper;
|
|
4238
|
-
this.options = this.options.map((option) => {
|
|
4239
|
-
return mapper(option);
|
|
4240
|
-
});
|
|
4241
|
-
}
|
|
4242
|
-
else if (Object.keys(this.optionsMapper).length > 0) {
|
|
4243
|
-
this.options = dataMapper(this.options, this.optionsMapper);
|
|
4244
|
-
}
|
|
4245
|
-
}
|
|
4246
|
-
const options = this.options.map(async (option) => {
|
|
4247
|
-
const text = await this.translate((!option.text.includes('options') ?
|
|
4248
|
-
getLocaleContextByKey(`${this.label.toLowerCase().replace('label', 'options')}`, option.text)
|
|
4249
|
-
: option.text));
|
|
4250
|
-
return {
|
|
4251
|
-
value: option.value,
|
|
4252
|
-
text: text
|
|
4253
|
-
};
|
|
4254
|
-
});
|
|
4255
|
-
return Promise.all(options);
|
|
4253
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4254
|
+
async ngOnInit(model) {
|
|
4255
|
+
// dont call super.ngOnInit to model conflicts
|
|
4256
|
+
if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
|
|
4257
|
+
this.formGroup = undefined;
|
|
4258
|
+
this.initialized = true;
|
|
4256
4259
|
}
|
|
4257
4260
|
/**
|
|
4258
|
-
* @description Component
|
|
4259
|
-
* @summary
|
|
4260
|
-
*
|
|
4261
|
-
*
|
|
4261
|
+
* @description Component cleanup lifecycle method.
|
|
4262
|
+
* @summary Performs cleanup operations when the component is destroyed.
|
|
4263
|
+
* Unregisters the FormGroup from the NgxFormService to prevent memory leaks
|
|
4264
|
+
* and ensure proper resource cleanup.
|
|
4262
4265
|
*
|
|
4263
4266
|
* @returns {void}
|
|
4264
|
-
* @memberOf CrudFieldComponent
|
|
4265
4267
|
*/
|
|
4266
|
-
|
|
4267
|
-
if (
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4268
|
+
ngOnDestroy() {
|
|
4269
|
+
if (this.formGroup)
|
|
4270
|
+
NgxFormService.unregister(this.formGroup);
|
|
4271
|
+
}
|
|
4272
|
+
getFormArrayIndex(index) {
|
|
4273
|
+
if (!(this.formGroup instanceof FormArray) && this.formGroup)
|
|
4274
|
+
return this.formGroup;
|
|
4275
|
+
const formGroup = this.formGroup.at(index);
|
|
4276
|
+
if (formGroup) {
|
|
4277
|
+
if (this.children.length) {
|
|
4278
|
+
const children = [...this.children];
|
|
4279
|
+
this.children = [];
|
|
4280
|
+
this.changeDetectorRef.detectChanges();
|
|
4281
|
+
this.children = [...children.map(child => {
|
|
4282
|
+
const props = (child.props || {});
|
|
4283
|
+
const name = props.name;
|
|
4284
|
+
const control = formGroup.get(name);
|
|
4285
|
+
child.props.value = control?.value;
|
|
4286
|
+
child.props.formGroup = formGroup;
|
|
4287
|
+
child.props.activeFormGroupIndex = index;
|
|
4288
|
+
child.props.formControl = control;
|
|
4289
|
+
return child;
|
|
4290
|
+
})];
|
|
4291
|
+
this.changeDetectorRef.detectChanges();
|
|
4292
|
+
}
|
|
4273
4293
|
}
|
|
4294
|
+
return formGroup || undefined;
|
|
4274
4295
|
}
|
|
4275
4296
|
/**
|
|
4276
|
-
* @description
|
|
4277
|
-
* @summary
|
|
4278
|
-
*
|
|
4279
|
-
*
|
|
4297
|
+
* @description Handles form reset or navigation back functionality.
|
|
4298
|
+
* @summary Provides different reset behavior based on the current operation.
|
|
4299
|
+
* For CREATE and UPDATE operations, resets the form to its initial state.
|
|
4300
|
+
* For READ and DELETE operations, navigates back in the browser history
|
|
4301
|
+
* since these operations don't have modifiable form data to reset.
|
|
4280
4302
|
*
|
|
4281
4303
|
* @returns {void}
|
|
4282
|
-
* @memberOf CrudFieldComponent
|
|
4283
4304
|
*/
|
|
4305
|
+
handleReset() {
|
|
4306
|
+
if (![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation) && this.allowClear)
|
|
4307
|
+
return NgxFormService.reset(this.formGroup);
|
|
4308
|
+
this.location.back();
|
|
4309
|
+
}
|
|
4310
|
+
async handleSubmit(event, eventName, componentName) {
|
|
4311
|
+
if (event) {
|
|
4312
|
+
event.preventDefault();
|
|
4313
|
+
event.stopImmediatePropagation();
|
|
4314
|
+
}
|
|
4315
|
+
if (!NgxFormService.validateFields(this.formGroup))
|
|
4316
|
+
return false;
|
|
4317
|
+
const data = NgxFormService.getFormData(this.formGroup);
|
|
4318
|
+
this.submitEvent.emit({
|
|
4319
|
+
data,
|
|
4320
|
+
component: componentName || this.componentName,
|
|
4321
|
+
name: eventName || this.action || EventConstants.SUBMIT,
|
|
4322
|
+
handlers: this.handlers,
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4326
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormDirective, isStandalone: true, inputs: { parentFormId: "parentFormId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", rendererId: "rendererId", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0 }); }
|
|
4327
|
+
}
|
|
4328
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, decorators: [{
|
|
4329
|
+
type: Directive
|
|
4330
|
+
}], propDecorators: { parentFormId: [{
|
|
4331
|
+
type: Input
|
|
4332
|
+
}], component: [{
|
|
4333
|
+
type: ViewChild,
|
|
4334
|
+
args: ['component', { static: false, read: ElementRef }]
|
|
4335
|
+
}], updateOn: [{
|
|
4336
|
+
type: Input
|
|
4337
|
+
}], target: [{
|
|
4338
|
+
type: Input
|
|
4339
|
+
}], method: [{
|
|
4340
|
+
type: Input
|
|
4341
|
+
}], options: [{
|
|
4342
|
+
type: Input
|
|
4343
|
+
}], action: [{
|
|
4344
|
+
type: Input
|
|
4345
|
+
}], operation: [{
|
|
4346
|
+
type: Input,
|
|
4347
|
+
args: [{ required: true }]
|
|
4348
|
+
}], handlers: [{
|
|
4349
|
+
type: Input
|
|
4350
|
+
}], formGroup: [{
|
|
4351
|
+
type: Input
|
|
4352
|
+
}], rendererId: [{
|
|
4353
|
+
type: Input
|
|
4354
|
+
}], submitEvent: [{
|
|
4355
|
+
type: Output
|
|
4356
|
+
}], allowClear: [{
|
|
4357
|
+
type: Input
|
|
4358
|
+
}] } });
|
|
4359
|
+
|
|
4360
|
+
class NgxMediaDirective extends NgxComponentDirective {
|
|
4361
|
+
// eslint-disable-next-line @angular-eslint/prefer-inject
|
|
4362
|
+
constructor(localeRoot = "NgxPageDirective") {
|
|
4363
|
+
super(localeRoot);
|
|
4364
|
+
this.destroy$ = new Subject();
|
|
4365
|
+
this.resizeSubject = new BehaviorSubject({
|
|
4366
|
+
width: window.innerWidth,
|
|
4367
|
+
height: window.innerHeight
|
|
4368
|
+
});
|
|
4369
|
+
this.resize$ = this.resizeSubject.asObservable();
|
|
4370
|
+
this.zone = inject(NgZone);
|
|
4371
|
+
// listen to window resize events outside Angular zone
|
|
4372
|
+
this.zone.runOutsideAngular(() => {
|
|
4373
|
+
fromEvent(window, 'resize')
|
|
4374
|
+
.pipe(takeUntil(this.destroy$))
|
|
4375
|
+
.subscribe(() => {
|
|
4376
|
+
const dimensions = {
|
|
4377
|
+
width: window.innerWidth,
|
|
4378
|
+
height: window.innerHeight
|
|
4379
|
+
};
|
|
4380
|
+
// update within the zone to reflect in Angular
|
|
4381
|
+
this.zone.run(() => this.resizeSubject.next(dimensions));
|
|
4382
|
+
});
|
|
4383
|
+
});
|
|
4384
|
+
// listen for color scheme changes
|
|
4385
|
+
this.colorScheme$ = new Observable(observer => {
|
|
4386
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
4387
|
+
const emit = () => observer.next(mediaQuery.matches ? 'dark' : 'light');
|
|
4388
|
+
emit(); // valor inicial
|
|
4389
|
+
mediaQuery.addEventListener('change', emit);
|
|
4390
|
+
return () => mediaQuery.removeEventListener('change', emit);
|
|
4391
|
+
}).pipe(takeUntil(this.destroy$));
|
|
4392
|
+
}
|
|
4393
|
+
async isDarkMode() {
|
|
4394
|
+
return await firstValueFrom(this.colorScheme$.pipe(map(scheme => scheme === 'dark')));
|
|
4395
|
+
}
|
|
4284
4396
|
ngOnDestroy() {
|
|
4285
|
-
|
|
4286
|
-
|
|
4397
|
+
this.destroy$.next();
|
|
4398
|
+
this.destroy$.complete();
|
|
4287
4399
|
}
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4400
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxMediaDirective, deps: [{ token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4401
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxMediaDirective, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
|
|
4402
|
+
}
|
|
4403
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxMediaDirective, decorators: [{
|
|
4404
|
+
type: Directive
|
|
4405
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
4406
|
+
type: Inject,
|
|
4407
|
+
args: [CPTKN]
|
|
4408
|
+
}] }] });
|
|
4409
|
+
|
|
4410
|
+
/**
|
|
4411
|
+
* @module engine
|
|
4412
|
+
* @description Angular rendering engine for Decaf applications
|
|
4413
|
+
* @summary The engine module provides core functionality for rendering Angular components
|
|
4414
|
+
* in Decaf applications. It includes constants, decorators, rendering engines, and utility types
|
|
4415
|
+
* that enable dynamic component creation, property mapping, and component lifecycle management.
|
|
4416
|
+
* Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
|
|
4417
|
+
* for component configuration.
|
|
4418
|
+
*/
|
|
4419
|
+
|
|
4420
|
+
/**
|
|
4421
|
+
* @description Dynamic component renderer for Decaf Angular applications.
|
|
4422
|
+
* @summary This component provides a flexible way to dynamically render Angular components
|
|
4423
|
+
* at runtime based on a tag name. It handles the creation, property binding, and event
|
|
4424
|
+
* subscription for dynamically loaded components. This is particularly useful for
|
|
4425
|
+
* building configurable UIs where components need to be determined at runtime.
|
|
4426
|
+
*
|
|
4427
|
+
* @component {ComponentRendererComponent}
|
|
4428
|
+
* @example
|
|
4429
|
+
* <ngx-decaf-component-renderer
|
|
4430
|
+
* [tag]="tag"
|
|
4431
|
+
* [globals]="globals"
|
|
4432
|
+
* (listenEvent)="listenEvent($event)">
|
|
4433
|
+
* </ngx-decaf-component-renderer>
|
|
4434
|
+
*
|
|
4435
|
+
* @mermaid
|
|
4436
|
+
* classDiagram
|
|
4437
|
+
* class ComponentRendererComponent {
|
|
4438
|
+
* +ViewContainerRef vcr
|
|
4439
|
+
* +string tag
|
|
4440
|
+
* +Record~string, unknown~ globals
|
|
4441
|
+
* +EnvironmentInjector injector
|
|
4442
|
+
* +ComponentRef~unknown~ component
|
|
4443
|
+
* +EventEmitter~IBaseCustomEvent~ listenEvent
|
|
4444
|
+
* +ngOnInit()
|
|
4445
|
+
* +ngOnDestroy()
|
|
4446
|
+
* +ngOnChanges(changes)
|
|
4447
|
+
* -createComponent(tag, globals)
|
|
4448
|
+
* -subscribeEvents()
|
|
4449
|
+
* -unsubscribeEvents()
|
|
4450
|
+
* }
|
|
4451
|
+
* ComponentRendererComponent --|> OnInit
|
|
4452
|
+
* ComponentRendererComponent --|> OnChanges
|
|
4453
|
+
* ComponentRendererComponent --|> OnDestroy
|
|
4454
|
+
*
|
|
4455
|
+
* @implements {OnInit}
|
|
4456
|
+
* @implements {OnChanges}
|
|
4457
|
+
* @implements {OnDestroy}
|
|
4458
|
+
*/
|
|
4459
|
+
class ComponentRendererComponent {
|
|
4460
|
+
/**
|
|
4461
|
+
* @description Creates an instance of ComponentRendererComponent.
|
|
4462
|
+
* @summary Initializes a new ComponentRendererComponent. This component doesn't require
|
|
4463
|
+
* any dependencies to be injected in its constructor as it uses the inject function to
|
|
4464
|
+
* obtain the EnvironmentInjector.
|
|
4465
|
+
*
|
|
4466
|
+
* @memberOf ComponentRendererComponent
|
|
4467
|
+
*/
|
|
4468
|
+
constructor() {
|
|
4469
|
+
/**
|
|
4470
|
+
* @description Global properties to pass to the rendered component.
|
|
4471
|
+
* @summary This input property allows passing a set of properties to the dynamically
|
|
4472
|
+
* rendered component. These properties will be mapped to the component's inputs if they
|
|
4473
|
+
* match. Properties that don't match any input on the target component will be filtered out
|
|
4474
|
+
* with a warning.
|
|
4475
|
+
*
|
|
4476
|
+
* @type {Record<string, unknown>}
|
|
4477
|
+
* @default {}
|
|
4478
|
+
* @memberOf ComponentRendererComponent
|
|
4479
|
+
*/
|
|
4480
|
+
this.globals = {};
|
|
4481
|
+
/**
|
|
4482
|
+
* @description Injector used for dependency injection in the dynamic component.
|
|
4483
|
+
* @summary This injector is used when creating the dynamic component to provide it with
|
|
4484
|
+
* access to the application's dependency injection system. It ensures that the dynamically
|
|
4485
|
+
* created component can access the same services and dependencies as statically created
|
|
4486
|
+
* components.
|
|
4487
|
+
*
|
|
4488
|
+
* @type {EnvironmentInjector}
|
|
4489
|
+
* @memberOf ComponentRendererComponent
|
|
4490
|
+
*/
|
|
4491
|
+
this.injector = inject(EnvironmentInjector);
|
|
4492
|
+
this.children = [];
|
|
4493
|
+
this.projectable = true;
|
|
4494
|
+
/**
|
|
4495
|
+
* @description Event emitter for events from the rendered component.
|
|
4496
|
+
* @summary This output property emits events that originate from the dynamically rendered
|
|
4497
|
+
* component. It allows the parent component to listen for and respond to events from the
|
|
4498
|
+
* dynamic component, creating a communication channel between the parent and the dynamically
|
|
4499
|
+
* rendered child.
|
|
4500
|
+
*
|
|
4501
|
+
* @type {EventEmitter<IBaseCustomEvent>}
|
|
4502
|
+
* @memberOf ComponentRendererComponent
|
|
4503
|
+
*/
|
|
4504
|
+
this.listenEvent = new EventEmitter();
|
|
4505
|
+
this.parent = undefined;
|
|
4506
|
+
this.uid = generateRandomValue(12);
|
|
4507
|
+
this.logger = getLogger(this);
|
|
4508
|
+
}
|
|
4509
|
+
/**
|
|
4510
|
+
* @description Initializes the component after Angular first displays the data-bound properties.
|
|
4511
|
+
* @summary Sets up the component by creating the dynamic component specified by the tag input.
|
|
4512
|
+
* This method is called once when the component is initialized and triggers the dynamic
|
|
4513
|
+
* component creation process with the provided tag name and global properties.
|
|
4514
|
+
*
|
|
4515
|
+
* @mermaid
|
|
4516
|
+
* sequenceDiagram
|
|
4517
|
+
* participant A as Angular Lifecycle
|
|
4518
|
+
* participant C as ComponentRendererComponent
|
|
4519
|
+
* participant R as NgxRenderingEngine
|
|
4520
|
+
*
|
|
4521
|
+
* A->>C: ngOnInit()
|
|
4522
|
+
* C->>C: createComponent(tag, globals)
|
|
4523
|
+
* C->>R: components(tag)
|
|
4524
|
+
* R-->>C: Return component constructor
|
|
4525
|
+
* C->>C: Process component inputs
|
|
4526
|
+
* C->>C: Create component instance
|
|
4527
|
+
* C->>C: subscribeEvents()
|
|
4528
|
+
*
|
|
4529
|
+
* @return {void}
|
|
4530
|
+
* @memberOf ComponentRendererComponent
|
|
4531
|
+
*/
|
|
4532
|
+
ngOnInit() {
|
|
4533
|
+
if (!this.parent) {
|
|
4534
|
+
this.createComponent(this.tag, this.globals);
|
|
4294
4535
|
}
|
|
4295
4536
|
else {
|
|
4296
|
-
|
|
4537
|
+
this.createParentComponent();
|
|
4297
4538
|
}
|
|
4298
|
-
this.setValue(value);
|
|
4299
|
-
this.formControl.updateValueAndValidity();
|
|
4300
4539
|
}
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4540
|
+
/**
|
|
4541
|
+
* @description Cleans up resources when the component is destroyed.
|
|
4542
|
+
* @summary Performs cleanup operations when the component is being destroyed by Angular.
|
|
4543
|
+
* This includes unsubscribing from all event emitters of the dynamic component and
|
|
4544
|
+
* destroying the rendering engine instance to prevent memory leaks.
|
|
4545
|
+
*
|
|
4546
|
+
* @mermaid
|
|
4547
|
+
* sequenceDiagram
|
|
4548
|
+
* participant A as Angular Lifecycle
|
|
4549
|
+
* participant C as ComponentRendererComponent
|
|
4550
|
+
* participant R as NgxRenderingEngine
|
|
4551
|
+
*
|
|
4552
|
+
* A->>C: ngOnDestroy()
|
|
4553
|
+
* alt component exists
|
|
4554
|
+
* C->>C: unsubscribeEvents()
|
|
4555
|
+
* C->>R: destroy()
|
|
4556
|
+
* end
|
|
4557
|
+
*
|
|
4558
|
+
* @return {Promise<void>} A promise that resolves when cleanup is complete
|
|
4559
|
+
* @memberOf ComponentRendererComponent
|
|
4560
|
+
*/
|
|
4561
|
+
async ngOnDestroy() {
|
|
4562
|
+
if (this.component) {
|
|
4563
|
+
this.unsubscribeEvents();
|
|
4564
|
+
NgxRenderingEngine.destroy();
|
|
4565
|
+
}
|
|
4305
4566
|
}
|
|
4306
4567
|
/**
|
|
4307
|
-
* @description
|
|
4308
|
-
* @summary
|
|
4309
|
-
*
|
|
4310
|
-
*
|
|
4568
|
+
* @description Creates and renders a dynamic component.
|
|
4569
|
+
* @summary This method handles the creation of a dynamic component based on the provided tag.
|
|
4570
|
+
* It retrieves the component constructor from the rendering engine, processes its inputs,
|
|
4571
|
+
* filters out unmapped properties, creates the component instance, and sets up event subscriptions.
|
|
4572
|
+
*
|
|
4573
|
+
* @param {string} tag - The tag name of the component to create
|
|
4574
|
+
* @param {KeyValue} globals - Global properties to pass to the component
|
|
4575
|
+
* @return {void}
|
|
4576
|
+
*
|
|
4577
|
+
* @mermaid
|
|
4578
|
+
* sequenceDiagram
|
|
4579
|
+
* participant C as ComponentRendererComponent
|
|
4580
|
+
* participant R as NgxRenderingEngine
|
|
4581
|
+
* participant V as ViewContainerRef
|
|
4582
|
+
*
|
|
4583
|
+
* C->>R: components(tag)
|
|
4584
|
+
* R-->>C: Return component constructor
|
|
4585
|
+
* C->>C: reflectComponentType(component)
|
|
4586
|
+
* C->>C: Process input properties
|
|
4587
|
+
* C->>C: Filter unmapped properties
|
|
4588
|
+
* C->>V: clear()
|
|
4589
|
+
* C->>R: createComponent(component, props, metadata, vcr, injector, [])
|
|
4590
|
+
* R-->>C: Return component reference
|
|
4591
|
+
* C->>C: subscribeEvents()
|
|
4592
|
+
*
|
|
4593
|
+
* @private
|
|
4594
|
+
* @memberOf ComponentRendererComponent
|
|
4595
|
+
*/
|
|
4596
|
+
createComponent(tag, globals = {}) {
|
|
4597
|
+
const component = NgxRenderingEngine.components(tag)
|
|
4598
|
+
?.constructor;
|
|
4599
|
+
const metadata = reflectComponentType(component);
|
|
4600
|
+
const componentInputs = metadata.inputs;
|
|
4601
|
+
const props = globals?.['item'] || globals?.['props'] || {};
|
|
4602
|
+
if (props?.['tag'])
|
|
4603
|
+
delete props['tag'];
|
|
4604
|
+
if (props?.['children'] && !this.children.length)
|
|
4605
|
+
this.children = props['children'];
|
|
4606
|
+
props['children'] = this.children || [];
|
|
4607
|
+
const inputKeys = Object.keys(props);
|
|
4608
|
+
const unmappedKeys = [];
|
|
4609
|
+
for (const input of inputKeys) {
|
|
4610
|
+
if (!inputKeys.length)
|
|
4611
|
+
break;
|
|
4612
|
+
const prop = componentInputs.find((item) => item.propName === input);
|
|
4613
|
+
if (!prop) {
|
|
4614
|
+
delete props[input];
|
|
4615
|
+
unmappedKeys.push(input);
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
const hasChildrenInput = Object.values(componentInputs).some(({ propName }) => propName === AngularEngineKeys.CHILDREN);
|
|
4619
|
+
if (!this.projectable && hasChildrenInput)
|
|
4620
|
+
props[AngularEngineKeys.CHILDREN] = this.children;
|
|
4621
|
+
const hasRootForm = Object.values(componentInputs).some(({ propName }) => propName === BaseComponentProps.PARENT_COMPONENT);
|
|
4622
|
+
if (hasRootForm && this.parentComponent)
|
|
4623
|
+
props[BaseComponentProps.PARENT_COMPONENT] = this.parentComponent;
|
|
4624
|
+
this.vcr.clear();
|
|
4625
|
+
// const projectable = (this.children?.length && this.projectable);
|
|
4626
|
+
// const template = projectable ? this.vcr.createEmbeddedView(this.inner as TemplateRef<unknown>, this.injector).rootNodes : [];
|
|
4627
|
+
this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, []);
|
|
4628
|
+
this.subscribeEvents();
|
|
4629
|
+
}
|
|
4630
|
+
createParentComponent() {
|
|
4631
|
+
const { component, inputs } = this.parent;
|
|
4632
|
+
const metadata = reflectComponentType(component);
|
|
4633
|
+
const template = this.projectable ? this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes : [];
|
|
4634
|
+
this.component = NgxRenderingEngine.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
|
|
4635
|
+
this.subscribeEvents();
|
|
4636
|
+
}
|
|
4637
|
+
/**
|
|
4638
|
+
* @description Subscribes to events emitted by the dynamic component.
|
|
4639
|
+
* @summary This method sets up subscriptions to all EventEmitter properties of the
|
|
4640
|
+
* dynamically created component. When an event is emitted by the dynamic component,
|
|
4641
|
+
* it is captured and re-emitted through the listenEvent output property with additional
|
|
4642
|
+
* metadata about the event source.
|
|
4643
|
+
*
|
|
4644
|
+
* @mermaid
|
|
4645
|
+
* sequenceDiagram
|
|
4646
|
+
* participant C as ComponentRendererComponent
|
|
4647
|
+
* participant D as Dynamic Component
|
|
4648
|
+
* participant P as Parent Component
|
|
4649
|
+
*
|
|
4650
|
+
* C->>C: subscribeEvents()
|
|
4651
|
+
* C->>D: Get instance properties
|
|
4652
|
+
* loop For each property
|
|
4653
|
+
* C->>C: Check if property is EventEmitter
|
|
4654
|
+
* alt is EventEmitter
|
|
4655
|
+
* C->>D: Subscribe to event
|
|
4656
|
+
* D-->>C: Event emitted
|
|
4657
|
+
* C->>P: Re-emit event with metadata
|
|
4658
|
+
* end
|
|
4659
|
+
* end
|
|
4660
|
+
*
|
|
4661
|
+
* @private
|
|
4662
|
+
* @return {void}
|
|
4663
|
+
* @memberOf ComponentRendererComponent
|
|
4664
|
+
*/
|
|
4665
|
+
subscribeEvents() {
|
|
4666
|
+
if (this.component) {
|
|
4667
|
+
const instance = this.component?.instance;
|
|
4668
|
+
const componentKeys = Object.keys(instance);
|
|
4669
|
+
for (const key of componentKeys) {
|
|
4670
|
+
const value = instance[key];
|
|
4671
|
+
if (value instanceof EventEmitter)
|
|
4672
|
+
instance[key].subscribe((event) => {
|
|
4673
|
+
this.listenEvent.emit({
|
|
4674
|
+
name: key,
|
|
4675
|
+
...event,
|
|
4676
|
+
});
|
|
4677
|
+
});
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
/**
|
|
4682
|
+
* @description Unsubscribes from all events of the dynamic component.
|
|
4683
|
+
* @summary This method cleans up event subscriptions when the component is being destroyed.
|
|
4684
|
+
* It iterates through all properties of the dynamic component instance and unsubscribes
|
|
4685
|
+
* from any EventEmitter properties to prevent memory leaks and unexpected behavior after
|
|
4686
|
+
* the component is destroyed.
|
|
4687
|
+
*
|
|
4688
|
+
* @mermaid
|
|
4689
|
+
* sequenceDiagram
|
|
4690
|
+
* participant C as ComponentRendererComponent
|
|
4691
|
+
* participant D as Dynamic Component
|
|
4692
|
+
*
|
|
4693
|
+
* C->>C: unsubscribeEvents()
|
|
4694
|
+
* C->>D: Get instance properties
|
|
4695
|
+
* loop For each property
|
|
4696
|
+
* C->>C: Check if property is EventEmitter
|
|
4697
|
+
* alt is EventEmitter
|
|
4698
|
+
* C->>D: Unsubscribe from event
|
|
4699
|
+
* end
|
|
4700
|
+
* end
|
|
4311
4701
|
*
|
|
4312
|
-
* @
|
|
4313
|
-
* @
|
|
4314
|
-
* @memberOf
|
|
4702
|
+
* @private
|
|
4703
|
+
* @return {void}
|
|
4704
|
+
* @memberOf ComponentRendererComponent
|
|
4315
4705
|
*/
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4706
|
+
unsubscribeEvents() {
|
|
4707
|
+
if (this.component) {
|
|
4708
|
+
const instance = this.component?.instance;
|
|
4709
|
+
const componentKeys = Object.keys(instance);
|
|
4710
|
+
for (const key of componentKeys) {
|
|
4711
|
+
const value = instance[key];
|
|
4712
|
+
if (value instanceof EventEmitter)
|
|
4713
|
+
instance[key].unsubscribe();
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4322
4716
|
}
|
|
4323
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
4324
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", 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", 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", multiple: "multiple", uid: "uid", page: "page", translatable: "translatable" }, host: { listeners: { "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!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.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--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(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-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(--dcf-color-gray-7)!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)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { 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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
4325
|
-
}
|
|
4326
|
-
|
|
4327
|
-
Dynamic()
|
|
4328
|
-
], CrudFieldComponent);
|
|
4329
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, decorators: [{
|
|
4717
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4718
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", children: "children", projectable: "projectable", model: "model", parentComponent: "parentComponent", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, 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: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<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 @if(projectable) {\n @if(children?.length) {\n @for(child of 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 <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
4719
|
+
}
|
|
4720
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, decorators: [{
|
|
4330
4721
|
type: Component,
|
|
4331
|
-
args: [{ standalone: true,
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
IonCheckbox,
|
|
4337
|
-
IonRadioGroup,
|
|
4338
|
-
IonRadio,
|
|
4339
|
-
IonSelect,
|
|
4340
|
-
IonSelectOption,
|
|
4341
|
-
IonLabel,
|
|
4342
|
-
IonText,
|
|
4343
|
-
IonTextarea
|
|
4344
|
-
], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!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.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--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(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-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(--dcf-color-gray-7)!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)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
|
|
4345
|
-
}], propDecorators: { operation: [{
|
|
4346
|
-
type: Input,
|
|
4347
|
-
args: [{ required: true }]
|
|
4348
|
-
}], name: [{
|
|
4349
|
-
type: Input,
|
|
4350
|
-
args: [{ required: true }]
|
|
4351
|
-
}], className: [{
|
|
4352
|
-
type: Input
|
|
4353
|
-
}], path: [{
|
|
4354
|
-
type: Input,
|
|
4355
|
-
args: [{ required: true }]
|
|
4356
|
-
}], childOf: [{
|
|
4357
|
-
type: Input
|
|
4358
|
-
}], type: [{
|
|
4359
|
-
type: Input,
|
|
4360
|
-
args: [{ required: true }]
|
|
4361
|
-
}], value: [{
|
|
4362
|
-
type: Input
|
|
4363
|
-
}], disabled: [{
|
|
4364
|
-
type: Input
|
|
4365
|
-
}], label: [{
|
|
4722
|
+
args: [{ selector: 'ngx-decaf-component-renderer', imports: [NgComponentOutlet], standalone: true, encapsulation: ViewEncapsulation.None, host: { '[attr.id]': 'uid' }, template: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<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 @if(projectable) {\n @if(children?.length) {\n @for(child of 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 <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n" }]
|
|
4723
|
+
}], ctorParameters: () => [], propDecorators: { vcr: [{
|
|
4724
|
+
type: ViewChild,
|
|
4725
|
+
args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
|
|
4726
|
+
}], tag: [{
|
|
4366
4727
|
type: Input,
|
|
4367
4728
|
args: [{ required: true }]
|
|
4368
|
-
}],
|
|
4369
|
-
type: Input
|
|
4370
|
-
}], format: [{
|
|
4371
|
-
type: Input
|
|
4372
|
-
}], hidden: [{
|
|
4373
|
-
type: Input
|
|
4374
|
-
}], max: [{
|
|
4375
|
-
type: Input
|
|
4376
|
-
}], maxlength: [{
|
|
4377
|
-
type: Input
|
|
4378
|
-
}], min: [{
|
|
4379
|
-
type: Input
|
|
4380
|
-
}], minlength: [{
|
|
4381
|
-
type: Input
|
|
4382
|
-
}], pattern: [{
|
|
4383
|
-
type: Input
|
|
4384
|
-
}], readonly: [{
|
|
4385
|
-
type: Input
|
|
4386
|
-
}], required: [{
|
|
4387
|
-
type: Input
|
|
4388
|
-
}], step: [{
|
|
4389
|
-
type: Input
|
|
4390
|
-
}], equals: [{
|
|
4391
|
-
type: Input
|
|
4392
|
-
}], different: [{
|
|
4393
|
-
type: Input
|
|
4394
|
-
}], lessThan: [{
|
|
4395
|
-
type: Input
|
|
4396
|
-
}], lessThanOrEqual: [{
|
|
4397
|
-
type: Input
|
|
4398
|
-
}], greaterThan: [{
|
|
4399
|
-
type: Input
|
|
4400
|
-
}], greaterThanOrEqual: [{
|
|
4401
|
-
type: Input
|
|
4402
|
-
}], alignment: [{
|
|
4403
|
-
type: Input
|
|
4404
|
-
}], checked: [{
|
|
4405
|
-
type: Input
|
|
4406
|
-
}], justify: [{
|
|
4407
|
-
type: Input
|
|
4408
|
-
}], cancelText: [{
|
|
4409
|
-
type: Input
|
|
4410
|
-
}], interface: [{
|
|
4411
|
-
type: Input
|
|
4412
|
-
}], options: [{
|
|
4413
|
-
type: Input
|
|
4414
|
-
}], mode: [{
|
|
4415
|
-
type: Input
|
|
4416
|
-
}], spellcheck: [{
|
|
4729
|
+
}], globals: [{
|
|
4417
4730
|
type: Input
|
|
4418
|
-
}],
|
|
4731
|
+
}], children: [{
|
|
4419
4732
|
type: Input
|
|
4420
|
-
}],
|
|
4733
|
+
}], projectable: [{
|
|
4421
4734
|
type: Input
|
|
4422
|
-
}],
|
|
4735
|
+
}], listenEvent: [{
|
|
4736
|
+
type: Output
|
|
4737
|
+
}], model: [{
|
|
4423
4738
|
type: Input
|
|
4424
|
-
}],
|
|
4739
|
+
}], parentComponent: [{
|
|
4425
4740
|
type: Input
|
|
4426
|
-
}],
|
|
4741
|
+
}], parent: [{
|
|
4427
4742
|
type: Input
|
|
4428
|
-
}],
|
|
4743
|
+
}], inner: [{
|
|
4429
4744
|
type: ViewChild,
|
|
4430
|
-
args: ['
|
|
4431
|
-
}], formGroup: [{
|
|
4432
|
-
type: Input
|
|
4433
|
-
}], formControl: [{
|
|
4434
|
-
type: Input
|
|
4435
|
-
}], multiple: [{
|
|
4436
|
-
type: Input
|
|
4437
|
-
}], uid: [{
|
|
4438
|
-
type: Input
|
|
4439
|
-
}], page: [{
|
|
4440
|
-
type: Input
|
|
4441
|
-
}], translatable: [{
|
|
4442
|
-
type: Input
|
|
4443
|
-
}], handleFieldsetUpdateGroupEvent: [{
|
|
4444
|
-
type: HostListener,
|
|
4445
|
-
args: ['window:fieldsetUpdateGroupEvent', ['$event']]
|
|
4745
|
+
args: ['inner', { read: TemplateRef, static: true }]
|
|
4446
4746
|
}] } });
|
|
4447
4747
|
|
|
4448
4748
|
/**
|
|
4449
|
-
* @
|
|
4450
|
-
* @
|
|
4451
|
-
*
|
|
4452
|
-
*
|
|
4749
|
+
* @description A dynamic form field component for CRUD operations.
|
|
4750
|
+
* @summary The CrudFieldComponent is a versatile form field component that adapts to different
|
|
4751
|
+
* input types and CRUD operations. It extends NgxFormFieldDirective to inherit form handling capabilities
|
|
4752
|
+
* and implements lifecycle hooks to properly initialize, render, and clean up. This component
|
|
4753
|
+
* supports various input types (text, number, date, select, etc.), validation rules, and styling
|
|
4754
|
+
* options, making it suitable for building dynamic forms for create, read, update, and delete
|
|
4755
|
+
* operations.
|
|
4453
4756
|
*
|
|
4454
|
-
* @
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
* @
|
|
4458
|
-
* @
|
|
4459
|
-
*
|
|
4460
|
-
*
|
|
4461
|
-
*
|
|
4757
|
+
* @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
|
|
4758
|
+
* @param {string} name - The field name, used as form control identifier
|
|
4759
|
+
* @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
|
|
4760
|
+
* @param {string|number|Date} value - The initial value of the field
|
|
4761
|
+
* @param {boolean} disabled - Whether the field is disabled
|
|
4762
|
+
* @param {string} label - The display label for the field
|
|
4763
|
+
* @param {string} placeholder - Placeholder text when field is empty
|
|
4764
|
+
* @param {string} format - Format pattern for the field value
|
|
4765
|
+
* @param {boolean} hidden - Whether the field should be hidden
|
|
4766
|
+
* @param {number|Date} max - Maximum allowed value
|
|
4767
|
+
* @param {number} maxlength - Maximum allowed length
|
|
4768
|
+
* @param {number|Date} min - Minimum allowed value
|
|
4769
|
+
* @param {number} minlength - Minimum allowed length
|
|
4770
|
+
* @param {string} pattern - Validation pattern
|
|
4771
|
+
* @param {boolean} readonly - Whether the field is read-only
|
|
4772
|
+
* @param {boolean} required - Whether the field is required
|
|
4773
|
+
* @param {number} step - Step value for number inputs
|
|
4774
|
+
* @param {FormGroup} formGroup - The parent form group
|
|
4775
|
+
* @param {StringOrBoolean} translatable - Whether field labels should be translated
|
|
4462
4776
|
*
|
|
4463
|
-
* @
|
|
4464
|
-
* @
|
|
4465
|
-
*
|
|
4777
|
+
* @component CrudFieldComponent
|
|
4778
|
+
* @example
|
|
4779
|
+
* <ngx-decaf-crud-field
|
|
4780
|
+
* operation="create"
|
|
4781
|
+
* name="firstName"
|
|
4782
|
+
* type="text"
|
|
4783
|
+
* label="<NAME>"
|
|
4784
|
+
* placeholder="<NAME>"
|
|
4785
|
+
* [value]="model.firstName"
|
|
4786
|
+
* [disabled]="model.readOnly">
|
|
4787
|
+
*
|
|
4788
|
+
*
|
|
4789
|
+
* @memberOf module:for-angular
|
|
4466
4790
|
*/
|
|
4467
|
-
class
|
|
4791
|
+
let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective {
|
|
4468
4792
|
constructor() {
|
|
4469
4793
|
super(...arguments);
|
|
4794
|
+
this.className = 'dcf-width-1-1';
|
|
4470
4795
|
/**
|
|
4471
|
-
* @description
|
|
4472
|
-
* @summary
|
|
4473
|
-
* the structure, validation, and presentation of form fields across all pages.
|
|
4474
|
-
* Each metadata object contains information about field type, validation rules,
|
|
4475
|
-
* page assignment, and display properties.
|
|
4796
|
+
* @description The parent field path, if this field is nested.
|
|
4797
|
+
* @summary Specifies the full dot-delimited path of the parent field. This is only set when the field is nested.
|
|
4476
4798
|
*
|
|
4477
|
-
* @type {
|
|
4799
|
+
* @type {string}
|
|
4800
|
+
* @memberOf CrudFieldComponent
|
|
4801
|
+
*/
|
|
4802
|
+
/**
|
|
4803
|
+
* @description The parent field path for nested field structures.
|
|
4804
|
+
* @summary Specifies the full dot-delimited path of the parent field when this field
|
|
4805
|
+
* is part of a nested structure. This is used for hierarchical form organization
|
|
4806
|
+
* and proper form control resolution in complex form structures.
|
|
4807
|
+
*
|
|
4808
|
+
* @type {string}
|
|
4809
|
+
* @default ''
|
|
4810
|
+
* @memberOf CrudFieldComponent
|
|
4811
|
+
*/
|
|
4812
|
+
this.childOf = '';
|
|
4813
|
+
/**
|
|
4814
|
+
* @description The initial value of the field.
|
|
4815
|
+
* @summary Sets the initial value of the form field. This can be a string, number, or Date
|
|
4816
|
+
* depending on the field type. For select fields, this should match one of the option values.
|
|
4817
|
+
*
|
|
4818
|
+
* @type {string | number | Date}
|
|
4819
|
+
* @default ''
|
|
4820
|
+
* @memberOf CrudFieldComponent
|
|
4821
|
+
*/
|
|
4822
|
+
this.value = '';
|
|
4823
|
+
/**
|
|
4824
|
+
* @description Whether the field should be hidden.
|
|
4825
|
+
* @summary When true, the field will not be visible in the UI but will still be part of the form model.
|
|
4826
|
+
* This is useful for fields that need to be included in form submission but should not be displayed to the user.
|
|
4827
|
+
*
|
|
4828
|
+
* @type {boolean}
|
|
4829
|
+
* @memberOf CrudFieldComponent
|
|
4830
|
+
*/
|
|
4831
|
+
this.hidden = false;
|
|
4832
|
+
/**
|
|
4833
|
+
* @description Interface style for select inputs.
|
|
4834
|
+
* @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
|
|
4835
|
+
* This determines how the select options are presented to the user.
|
|
4836
|
+
*
|
|
4837
|
+
* @type {SelectInterface}
|
|
4838
|
+
* @memberOf CrudFieldComponent
|
|
4478
4839
|
*/
|
|
4479
|
-
this.
|
|
4840
|
+
this.interface = 'popover';
|
|
4480
4841
|
/**
|
|
4481
|
-
* @description
|
|
4482
|
-
* @summary
|
|
4483
|
-
*
|
|
4484
|
-
* column properties or sizing. This allows for flexible grid layouts that can adapt to
|
|
4485
|
-
* different content requirements.
|
|
4842
|
+
* @description Spellcheck attribute for text inputs.
|
|
4843
|
+
* @summary Enables or disables spellchecking for text inputs.
|
|
4844
|
+
* When true, the browser will check the spelling of the input text.
|
|
4486
4845
|
*
|
|
4487
|
-
* @type {
|
|
4488
|
-
* @default
|
|
4846
|
+
* @type {boolean}
|
|
4847
|
+
* @default false
|
|
4848
|
+
* @memberOf CrudFieldComponent
|
|
4489
4849
|
*/
|
|
4490
|
-
this.
|
|
4850
|
+
this.spellcheck = false;
|
|
4491
4851
|
/**
|
|
4492
|
-
* @description
|
|
4493
|
-
* @summary
|
|
4494
|
-
*
|
|
4495
|
-
* row properties or sizing. This provides control over vertical spacing and content organization.
|
|
4852
|
+
* @description Input mode for text inputs.
|
|
4853
|
+
* @summary Hints at the type of data that might be entered by the user while editing the element.
|
|
4854
|
+
* This can affect the virtual keyboard layout on mobile devices.
|
|
4496
4855
|
*
|
|
4497
|
-
* @type {
|
|
4498
|
-
* @default
|
|
4856
|
+
* @type {'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'}
|
|
4857
|
+
* @default 'none'
|
|
4858
|
+
* @memberOf CrudFieldComponent
|
|
4499
4859
|
*/
|
|
4500
|
-
this.
|
|
4501
|
-
}
|
|
4502
|
-
async ngOnInit(model) {
|
|
4503
|
-
if (model)
|
|
4504
|
-
this.model = model;
|
|
4505
|
-
if (this.model && !this.repository)
|
|
4506
|
-
this._repository = this.repository;
|
|
4507
|
-
}
|
|
4508
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4509
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxParentComponentDirective, isStandalone: true, inputs: { parentComponent: "parentComponent", children: "children", cols: "cols", rows: "rows" }, usesInheritance: true, ngImport: i0 }); }
|
|
4510
|
-
}
|
|
4511
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, decorators: [{
|
|
4512
|
-
type: Directive
|
|
4513
|
-
}], propDecorators: { parentComponent: [{
|
|
4514
|
-
type: Input
|
|
4515
|
-
}], children: [{
|
|
4516
|
-
type: Input
|
|
4517
|
-
}], cols: [{
|
|
4518
|
-
type: Input
|
|
4519
|
-
}], rows: [{
|
|
4520
|
-
type: Input
|
|
4521
|
-
}] } });
|
|
4522
|
-
|
|
4523
|
-
class NgxFormDirective extends NgxParentComponentDirective {
|
|
4524
|
-
constructor() {
|
|
4525
|
-
super(...arguments);
|
|
4526
|
-
this.crudFieldComponent = ComponentsTagNames.CRUD_FIELD;
|
|
4860
|
+
this.inputmode = 'none';
|
|
4527
4861
|
/**
|
|
4528
|
-
* @description
|
|
4529
|
-
* @summary
|
|
4530
|
-
*
|
|
4531
|
-
* when validation feedback is shown to the user during form interaction.
|
|
4862
|
+
* @description Autocomplete behavior for the field.
|
|
4863
|
+
* @summary Specifies whether and how the browser should automatically complete the input.
|
|
4864
|
+
* This can improve user experience by suggesting previously entered values.
|
|
4532
4865
|
*
|
|
4533
|
-
* @type {
|
|
4534
|
-
* @default '
|
|
4866
|
+
* @type {AutocompleteTypes}
|
|
4867
|
+
* @default 'off'
|
|
4868
|
+
* @memberOf CrudFieldComponent
|
|
4535
4869
|
*/
|
|
4536
|
-
this.
|
|
4870
|
+
this.autocomplete = 'off';
|
|
4537
4871
|
/**
|
|
4538
|
-
* @description
|
|
4539
|
-
* @summary
|
|
4540
|
-
*
|
|
4541
|
-
* '_top', or a named frame. Controls the browser behavior for form responses.
|
|
4872
|
+
* @description Fill style for the field.
|
|
4873
|
+
* @summary Determines the fill style of the field, such as 'outline' or 'solid'.
|
|
4874
|
+
* This affects the border and background of the field.
|
|
4542
4875
|
*
|
|
4543
|
-
* @type {
|
|
4544
|
-
* @default '
|
|
4876
|
+
* @type {'outline' | 'solid'}
|
|
4877
|
+
* @default 'outline'
|
|
4878
|
+
* @memberOf CrudFieldComponent
|
|
4545
4879
|
*/
|
|
4546
|
-
this.
|
|
4880
|
+
this.fill = 'outline';
|
|
4547
4881
|
/**
|
|
4548
|
-
* @description
|
|
4549
|
-
* @summary
|
|
4550
|
-
*
|
|
4551
|
-
* Angular event-driven submission for single-page application workflows.
|
|
4882
|
+
* @description Placement of the label relative to the field.
|
|
4883
|
+
* @summary Specifies where the label should be placed relative to the field.
|
|
4884
|
+
* Options include 'start', 'end', 'floating', 'stacked', and 'fixed'.
|
|
4552
4885
|
*
|
|
4553
|
-
* @type {'
|
|
4554
|
-
* @default '
|
|
4886
|
+
* @type {'start' | 'end' | 'floating' | 'stacked' | 'fixed'}
|
|
4887
|
+
* @default 'floating'
|
|
4888
|
+
* @memberOf CrudFieldComponent
|
|
4555
4889
|
*/
|
|
4556
|
-
this.
|
|
4890
|
+
this.labelPlacement = 'floating';
|
|
4557
4891
|
/**
|
|
4558
|
-
* @description
|
|
4559
|
-
* @summary
|
|
4560
|
-
*
|
|
4561
|
-
* The operation affects form state, button visibility, and submission logic.
|
|
4892
|
+
* @description Update mode for the field.
|
|
4893
|
+
* @summary Determines when the field value should be updated in the form model.
|
|
4894
|
+
* Options include 'change', 'blur', and 'submit'.
|
|
4562
4895
|
*
|
|
4563
|
-
* @type {
|
|
4564
|
-
* @
|
|
4896
|
+
* @type {FieldUpdateMode}
|
|
4897
|
+
* @default 'change'
|
|
4898
|
+
* @memberOf CrudFieldComponent
|
|
4565
4899
|
*/
|
|
4566
|
-
this.
|
|
4900
|
+
this.updateOn = 'change';
|
|
4567
4901
|
/**
|
|
4568
|
-
* @description
|
|
4569
|
-
* @summary
|
|
4570
|
-
*
|
|
4571
|
-
*
|
|
4902
|
+
* @description Indicates if this field supports multiple values.
|
|
4903
|
+
* @summary When true, this field can handle multiple values, typically used in
|
|
4904
|
+
* multi-select scenarios or when the field is part of a form array structure
|
|
4905
|
+
* that allows multiple entries of the same field type.
|
|
4572
4906
|
*
|
|
4573
|
-
* @type {
|
|
4907
|
+
* @type {boolean}
|
|
4908
|
+
* @default false
|
|
4909
|
+
* @memberOf CrudFieldComponent
|
|
4574
4910
|
*/
|
|
4575
|
-
this.
|
|
4911
|
+
this.multiple = false;
|
|
4576
4912
|
/**
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
this.submitEvent = new EventEmitter();
|
|
4913
|
+
* @description Unique identifier for the current record.
|
|
4914
|
+
* @summary A unique identifier for the current record being displayed or manipulated.
|
|
4915
|
+
* This is typically used in conjunction with the primary key for operations on specific records.
|
|
4916
|
+
*
|
|
4917
|
+
* @type {string | number}
|
|
4918
|
+
*/
|
|
4919
|
+
this.uid = generateRandomValue(12);
|
|
4585
4920
|
/**
|
|
4586
|
-
* @description
|
|
4587
|
-
* @summary
|
|
4588
|
-
*
|
|
4589
|
-
* The uid is used for tracking, referencing, and emitting events related to the current record, and may be used
|
|
4590
|
-
* in conjunction with the primary key for CRUD operations.
|
|
4921
|
+
* @description Translatability of field labels.
|
|
4922
|
+
* @summary Indicates whether the field labels should be translated based on the current language settings.
|
|
4923
|
+
* This is useful for applications supporting multiple languages.
|
|
4591
4924
|
*
|
|
4592
|
-
* @type {
|
|
4593
|
-
* @default
|
|
4925
|
+
* @type {StringOrBoolean}
|
|
4926
|
+
* @default true
|
|
4927
|
+
* @memberOf CrudFieldComponent
|
|
4594
4928
|
*/
|
|
4595
|
-
this.
|
|
4596
|
-
// /**
|
|
4597
|
-
// * @description Angular change detection service.
|
|
4598
|
-
// * @summary Injected service that provides manual control over change detection cycles.
|
|
4599
|
-
// * This is essential for ensuring that programmatic DOM changes (like setting accordion
|
|
4600
|
-
// * attributes) are properly reflected in the component's state and trigger appropriate
|
|
4601
|
-
// * view updates when modifications occur outside the normal Angular change detection flow.
|
|
4602
|
-
// *
|
|
4603
|
-
// * @protected
|
|
4604
|
-
// * @type {ChangeDetectorRef}
|
|
4605
|
-
// * @memberOf CrudFormComponent
|
|
4606
|
-
// */
|
|
4607
|
-
// protected changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
|
|
4608
|
-
// /**
|
|
4609
|
-
// * @description Angular Renderer2 service for safe DOM manipulation.
|
|
4610
|
-
// * @summary Injected service that provides a safe, platform-agnostic way to manipulate DOM elements.
|
|
4611
|
-
// * This service ensures proper handling of DOM operations across different platforms and environments,
|
|
4612
|
-
// * including server-side rendering and web workers.
|
|
4613
|
-
// *
|
|
4614
|
-
// * @protected
|
|
4615
|
-
// * @type {Renderer2}
|
|
4616
|
-
// * @memberOf CrudFormComponent
|
|
4617
|
-
// */
|
|
4618
|
-
// protected renderer: Renderer2 = inject(Renderer2);
|
|
4619
|
-
// /**
|
|
4620
|
-
// * @description Translation service for internationalization.
|
|
4621
|
-
// * @summary Injected service that provides translation capabilities for UI text.
|
|
4622
|
-
// * Used to translate button labels and validation messages based on the current locale.
|
|
4623
|
-
// *
|
|
4624
|
-
// * @protected
|
|
4625
|
-
// * @type {TranslateService}
|
|
4626
|
-
// * @memberOf CrudFormComponent
|
|
4627
|
-
// */
|
|
4628
|
-
// protected translateService: TranslateService = inject(TranslateService);
|
|
4629
|
-
this.activeFormGroupIndex = 0;
|
|
4929
|
+
this.translatable = true;
|
|
4630
4930
|
}
|
|
4631
|
-
|
|
4632
|
-
|
|
4931
|
+
/**
|
|
4932
|
+
* @description Component initialization lifecycle method.
|
|
4933
|
+
* @summary Initializes the field component based on the operation type and field configuration.
|
|
4934
|
+
* For READ and DELETE operations, removes the form group to make fields read-only.
|
|
4935
|
+
* For other operations, sets up icons, configures multi-value support if needed,
|
|
4936
|
+
* and sets default values for radio buttons if no value is provided.
|
|
4937
|
+
*
|
|
4938
|
+
* @returns {void}
|
|
4939
|
+
* @memberOf CrudFieldComponent
|
|
4940
|
+
*/
|
|
4941
|
+
async ngOnInit() {
|
|
4942
|
+
this.options = await this.getOptions();
|
|
4943
|
+
addIcons({ chevronDownOutline, chevronUpOutline });
|
|
4944
|
+
if (Array.isArray(this.hidden) && !this.hidden.includes(this.operation)) {
|
|
4945
|
+
this.hidden = false;
|
|
4946
|
+
}
|
|
4947
|
+
if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
|
|
4948
|
+
this.formGroup = undefined;
|
|
4949
|
+
}
|
|
4950
|
+
else {
|
|
4951
|
+
if (!this.parentComponent && this.formGroup instanceof FormGroup || this.formGroup instanceof FormArray)
|
|
4952
|
+
this.parentComponent = (this.formGroup.root || this.formControl.root);
|
|
4953
|
+
if (this.multiple) {
|
|
4954
|
+
this.formGroup = this.activeFormGroup;
|
|
4955
|
+
if (!this.parentComponent)
|
|
4956
|
+
this.parentComponent = this.formGroup.parent;
|
|
4957
|
+
this.formControl = this.formGroup.get(this.name);
|
|
4958
|
+
}
|
|
4959
|
+
if (this.type === HTML5InputTypes.CHECKBOX && Array.isArray(this.value)) {
|
|
4960
|
+
this.setValue(this.value);
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
/**
|
|
4965
|
+
* Returns a list of options for select or radio inputs, with their `text` property
|
|
4966
|
+
* localized if it does not already include the word 'options'. The localization key
|
|
4967
|
+
* is generated from the component's label, replacing 'label' with 'options'.
|
|
4968
|
+
*
|
|
4969
|
+
* @returns {CrudFieldOption[]} The array of parsed and localized options.
|
|
4970
|
+
* @memberOf CrudFieldComponent
|
|
4971
|
+
*/
|
|
4972
|
+
async getOptions() {
|
|
4973
|
+
if (!this.options)
|
|
4974
|
+
return [];
|
|
4975
|
+
if (this.options instanceof Function) {
|
|
4976
|
+
const repo = getModelRepository(this.options().name);
|
|
4977
|
+
this.options = await repo?.select().execute();
|
|
4978
|
+
}
|
|
4979
|
+
if (this.optionsMapper) {
|
|
4980
|
+
if (this.optionsMapper instanceof Function || typeof this.optionsMapper === 'function') {
|
|
4981
|
+
const mapper = this.optionsMapper;
|
|
4982
|
+
this.options = this.options.map((option) => {
|
|
4983
|
+
return mapper(option);
|
|
4984
|
+
});
|
|
4985
|
+
}
|
|
4986
|
+
else if (Object.keys(this.optionsMapper).length > 0) {
|
|
4987
|
+
this.options = dataMapper(this.options, this.optionsMapper);
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
const options = this.options.map(async (option) => {
|
|
4991
|
+
const text = await this.translate((!option.text.includes('options') ?
|
|
4992
|
+
getLocaleContextByKey(`${this.label.toLowerCase().replace('label', 'options')}`, option.text)
|
|
4993
|
+
: option.text));
|
|
4994
|
+
return {
|
|
4995
|
+
value: option.value,
|
|
4996
|
+
text: text
|
|
4997
|
+
};
|
|
4998
|
+
});
|
|
4999
|
+
return Promise.all(options);
|
|
4633
5000
|
}
|
|
4634
5001
|
/**
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
5002
|
+
* @description Component after view initialization lifecycle method.
|
|
5003
|
+
* @summary Calls the parent afterViewInit method for READ and DELETE operations.
|
|
5004
|
+
* This ensures proper initialization of read-only fields that don't require
|
|
5005
|
+
* form functionality but still need view setup.
|
|
5006
|
+
*
|
|
5007
|
+
* @returns {void}
|
|
5008
|
+
* @memberOf CrudFieldComponent
|
|
5009
|
+
*/
|
|
5010
|
+
ngAfterViewInit() {
|
|
5011
|
+
if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
|
|
5012
|
+
super.afterViewInit();
|
|
5013
|
+
}
|
|
5014
|
+
else {
|
|
5015
|
+
if (this.type === HTML5InputTypes.RADIO && !this.value)
|
|
5016
|
+
this.setValue(this.options[0].value); // TODO: migrate to RenderingEngine
|
|
5017
|
+
}
|
|
4649
5018
|
}
|
|
4650
5019
|
/**
|
|
4651
5020
|
* @description Component cleanup lifecycle method.
|
|
4652
|
-
* @summary Performs cleanup operations
|
|
4653
|
-
*
|
|
4654
|
-
*
|
|
5021
|
+
* @summary Performs cleanup operations for READ and DELETE operations by calling
|
|
5022
|
+
* the parent onDestroy method. This ensures proper resource cleanup for
|
|
5023
|
+
* read-only field components.
|
|
4655
5024
|
*
|
|
4656
5025
|
* @returns {void}
|
|
5026
|
+
* @memberOf CrudFieldComponent
|
|
4657
5027
|
*/
|
|
4658
5028
|
ngOnDestroy() {
|
|
4659
|
-
if (this.
|
|
4660
|
-
|
|
5029
|
+
if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
|
|
5030
|
+
this.onDestroy();
|
|
4661
5031
|
}
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
const children = [...this.children];
|
|
4669
|
-
this.children = [];
|
|
4670
|
-
this.changeDetectorRef.detectChanges();
|
|
4671
|
-
this.children = [...children.map(child => {
|
|
4672
|
-
const props = (child.props || {});
|
|
4673
|
-
const name = props.name;
|
|
4674
|
-
const control = formGroup.get(name);
|
|
4675
|
-
child.props.value = control?.value;
|
|
4676
|
-
child.props.formGroup = formGroup;
|
|
4677
|
-
child.props.activeFormGroupIndex = index;
|
|
4678
|
-
child.props.formControl = control;
|
|
4679
|
-
return child;
|
|
4680
|
-
})];
|
|
4681
|
-
this.changeDetectorRef.detectChanges();
|
|
4682
|
-
}
|
|
5032
|
+
toggleOptionSelection(val, event) {
|
|
5033
|
+
const { checked } = event.detail;
|
|
5034
|
+
let value = Array.isArray(this.formControl.value) ? this.formControl.value : [];
|
|
5035
|
+
if (checked) {
|
|
5036
|
+
if (!value.includes(val))
|
|
5037
|
+
value = [...value, val];
|
|
4683
5038
|
}
|
|
4684
|
-
|
|
5039
|
+
else {
|
|
5040
|
+
value = value.filter(v => v !== val);
|
|
5041
|
+
}
|
|
5042
|
+
this.setValue(value);
|
|
5043
|
+
this.formControl.updateValueAndValidity();
|
|
5044
|
+
}
|
|
5045
|
+
isOptionChecked(value) {
|
|
5046
|
+
if (!this.formControl.value || !Array.isArray(this.formControl.value))
|
|
5047
|
+
return false;
|
|
5048
|
+
return this.formControl.value.includes(value);
|
|
4685
5049
|
}
|
|
4686
5050
|
/**
|
|
4687
|
-
* @description Handles
|
|
4688
|
-
* @summary
|
|
4689
|
-
*
|
|
4690
|
-
*
|
|
4691
|
-
* since these operations don't have modifiable form data to reset.
|
|
5051
|
+
* @description Handles fieldset group update events from parent fieldsets.
|
|
5052
|
+
* @summary Processes events triggered when an existing group needs to be updated.
|
|
5053
|
+
* Updates the active form group index and refreshes the form group and form control
|
|
5054
|
+
* references to point to the group being edited.
|
|
4692
5055
|
*
|
|
5056
|
+
* @param {CustomEvent} event - The fieldset update group event containing update details
|
|
4693
5057
|
* @returns {void}
|
|
5058
|
+
* @memberOf CrudFieldComponent
|
|
4694
5059
|
*/
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
this.
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
if (event) {
|
|
4702
|
-
event.preventDefault();
|
|
4703
|
-
event.stopImmediatePropagation();
|
|
4704
|
-
}
|
|
4705
|
-
if (!NgxDecafFormService.validateFields(this.formGroup))
|
|
4706
|
-
return false;
|
|
4707
|
-
const data = NgxDecafFormService.getFormData(this.formGroup);
|
|
4708
|
-
this.submitEvent.emit({
|
|
4709
|
-
data,
|
|
4710
|
-
component: componentName || this.componentName,
|
|
4711
|
-
name: eventName || this.action || EventConstants.SUBMIT,
|
|
4712
|
-
handlers: this.handlers,
|
|
4713
|
-
});
|
|
5060
|
+
handleFieldsetUpdateGroupEvent(event) {
|
|
5061
|
+
const { formGroup, index } = event.detail;
|
|
5062
|
+
this.activeFormGroupIndex = index;
|
|
5063
|
+
this.formGroup = formGroup;
|
|
5064
|
+
this.formControl = this.formGroup.get(this.name);
|
|
5065
|
+
this.value = this.formControl.value;
|
|
4714
5066
|
}
|
|
4715
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type:
|
|
4716
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormDirective, isStandalone: true, inputs: { parentFormId: "parentFormId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", rendererId: "rendererId", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0 }); }
|
|
4717
|
-
}
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
5067
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
5068
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", 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", 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", multiple: "multiple", uid: "uid", page: "page", translatable: "translatable" }, host: { listeners: { "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!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.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--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(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-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(--dcf-color-gray-7)!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)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { 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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
5069
|
+
};
|
|
5070
|
+
CrudFieldComponent = __decorate([
|
|
5071
|
+
Dynamic()
|
|
5072
|
+
], CrudFieldComponent);
|
|
5073
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, decorators: [{
|
|
5074
|
+
type: Component,
|
|
5075
|
+
args: [{ standalone: true, imports: [
|
|
5076
|
+
ReactiveFormsModule,
|
|
5077
|
+
TranslatePipe,
|
|
5078
|
+
IonInput,
|
|
5079
|
+
IonItem,
|
|
5080
|
+
IonCheckbox,
|
|
5081
|
+
IonRadioGroup,
|
|
5082
|
+
IonRadio,
|
|
5083
|
+
IonSelect,
|
|
5084
|
+
IonSelectOption,
|
|
5085
|
+
IonLabel,
|
|
5086
|
+
IonText,
|
|
5087
|
+
IonTextarea
|
|
5088
|
+
], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!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.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--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(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-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(--dcf-color-gray-7)!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)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
|
|
5089
|
+
}], propDecorators: { operation: [{
|
|
5090
|
+
type: Input,
|
|
5091
|
+
args: [{ required: true }]
|
|
5092
|
+
}], name: [{
|
|
5093
|
+
type: Input,
|
|
5094
|
+
args: [{ required: true }]
|
|
5095
|
+
}], className: [{
|
|
4721
5096
|
type: Input
|
|
4722
|
-
}],
|
|
4723
|
-
type:
|
|
4724
|
-
args: [
|
|
4725
|
-
}],
|
|
5097
|
+
}], path: [{
|
|
5098
|
+
type: Input,
|
|
5099
|
+
args: [{ required: true }]
|
|
5100
|
+
}], childOf: [{
|
|
4726
5101
|
type: Input
|
|
4727
|
-
}],
|
|
5102
|
+
}], type: [{
|
|
5103
|
+
type: Input,
|
|
5104
|
+
args: [{ required: true }]
|
|
5105
|
+
}], value: [{
|
|
4728
5106
|
type: Input
|
|
4729
|
-
}],
|
|
5107
|
+
}], disabled: [{
|
|
5108
|
+
type: Input
|
|
5109
|
+
}], label: [{
|
|
5110
|
+
type: Input,
|
|
5111
|
+
args: [{ required: true }]
|
|
5112
|
+
}], placeholder: [{
|
|
5113
|
+
type: Input
|
|
5114
|
+
}], format: [{
|
|
5115
|
+
type: Input
|
|
5116
|
+
}], hidden: [{
|
|
5117
|
+
type: Input
|
|
5118
|
+
}], max: [{
|
|
5119
|
+
type: Input
|
|
5120
|
+
}], maxlength: [{
|
|
5121
|
+
type: Input
|
|
5122
|
+
}], min: [{
|
|
5123
|
+
type: Input
|
|
5124
|
+
}], minlength: [{
|
|
5125
|
+
type: Input
|
|
5126
|
+
}], pattern: [{
|
|
5127
|
+
type: Input
|
|
5128
|
+
}], readonly: [{
|
|
5129
|
+
type: Input
|
|
5130
|
+
}], required: [{
|
|
5131
|
+
type: Input
|
|
5132
|
+
}], step: [{
|
|
5133
|
+
type: Input
|
|
5134
|
+
}], equals: [{
|
|
5135
|
+
type: Input
|
|
5136
|
+
}], different: [{
|
|
5137
|
+
type: Input
|
|
5138
|
+
}], lessThan: [{
|
|
5139
|
+
type: Input
|
|
5140
|
+
}], lessThanOrEqual: [{
|
|
5141
|
+
type: Input
|
|
5142
|
+
}], greaterThan: [{
|
|
5143
|
+
type: Input
|
|
5144
|
+
}], greaterThanOrEqual: [{
|
|
5145
|
+
type: Input
|
|
5146
|
+
}], alignment: [{
|
|
5147
|
+
type: Input
|
|
5148
|
+
}], checked: [{
|
|
5149
|
+
type: Input
|
|
5150
|
+
}], justify: [{
|
|
5151
|
+
type: Input
|
|
5152
|
+
}], cancelText: [{
|
|
5153
|
+
type: Input
|
|
5154
|
+
}], interface: [{
|
|
4730
5155
|
type: Input
|
|
4731
5156
|
}], options: [{
|
|
4732
5157
|
type: Input
|
|
4733
|
-
}],
|
|
5158
|
+
}], mode: [{
|
|
4734
5159
|
type: Input
|
|
4735
|
-
}],
|
|
4736
|
-
type: Input
|
|
4737
|
-
|
|
4738
|
-
|
|
5160
|
+
}], spellcheck: [{
|
|
5161
|
+
type: Input
|
|
5162
|
+
}], inputmode: [{
|
|
5163
|
+
type: Input
|
|
5164
|
+
}], autocomplete: [{
|
|
4739
5165
|
type: Input
|
|
5166
|
+
}], fill: [{
|
|
5167
|
+
type: Input
|
|
5168
|
+
}], labelPlacement: [{
|
|
5169
|
+
type: Input
|
|
5170
|
+
}], updateOn: [{
|
|
5171
|
+
type: Input
|
|
5172
|
+
}], component: [{
|
|
5173
|
+
type: ViewChild,
|
|
5174
|
+
args: ['component', { read: ElementRef }]
|
|
4740
5175
|
}], formGroup: [{
|
|
4741
5176
|
type: Input
|
|
4742
|
-
}],
|
|
5177
|
+
}], formControl: [{
|
|
4743
5178
|
type: Input
|
|
4744
|
-
}],
|
|
4745
|
-
type:
|
|
4746
|
-
}],
|
|
5179
|
+
}], multiple: [{
|
|
5180
|
+
type: Input
|
|
5181
|
+
}], uid: [{
|
|
5182
|
+
type: Input
|
|
5183
|
+
}], page: [{
|
|
5184
|
+
type: Input
|
|
5185
|
+
}], translatable: [{
|
|
4747
5186
|
type: Input
|
|
5187
|
+
}], handleFieldsetUpdateGroupEvent: [{
|
|
5188
|
+
type: HostListener,
|
|
5189
|
+
args: ['window:fieldsetUpdateGroupEvent', ['$event']]
|
|
4748
5190
|
}] } });
|
|
4749
5191
|
|
|
4750
5192
|
/**
|
|
@@ -4784,7 +5226,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
4784
5226
|
* ModelRenderer->>ModelRenderer: Subscribe to events
|
|
4785
5227
|
* ModelRenderer-->>App: Emit events
|
|
4786
5228
|
*/
|
|
4787
|
-
class ModelRendererComponent extends
|
|
5229
|
+
class ModelRendererComponent extends NgxComponentDirective {
|
|
4788
5230
|
constructor() {
|
|
4789
5231
|
super(...arguments);
|
|
4790
5232
|
// /**
|
|
@@ -5032,7 +5474,7 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
|
|
|
5032
5474
|
this.initialized = true;
|
|
5033
5475
|
}
|
|
5034
5476
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5035
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { gap: "gap", breakpoint: "breakpoint", grid: "grid", match: "match" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
5477
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { gap: "gap", breakpoint: "breakpoint", grid: "grid", match: "match" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"((child.col === cols.length || row.cols.length === 1)? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
5036
5478
|
};
|
|
5037
5479
|
LayoutComponent = __decorate([
|
|
5038
5480
|
Dynamic(),
|
|
@@ -5040,7 +5482,7 @@ LayoutComponent = __decorate([
|
|
|
5040
5482
|
], LayoutComponent);
|
|
5041
5483
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, decorators: [{
|
|
5042
5484
|
type: Component,
|
|
5043
|
-
args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"] }]
|
|
5485
|
+
args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"((child.col === cols.length || row.cols.length === 1)? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"] }]
|
|
5044
5486
|
}], ctorParameters: () => [], propDecorators: { gap: [{
|
|
5045
5487
|
type: Input
|
|
5046
5488
|
}], breakpoint: [{
|
|
@@ -5072,7 +5514,7 @@ let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
|
|
|
5072
5514
|
/**
|
|
5073
5515
|
* @description Handles form submission with validation and event emission.
|
|
5074
5516
|
* @summary Processes form submission by first preventing default browser behavior,
|
|
5075
|
-
* then validating all form fields using
|
|
5517
|
+
* then validating all form fields using NgxFormService. If validation passes,
|
|
5076
5518
|
* extracts form data and emits a submitEvent with the data, component information,
|
|
5077
5519
|
* and any associated handlers. Returns false if validation fails.
|
|
5078
5520
|
*
|
|
@@ -5147,7 +5589,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
5147
5589
|
* @extends {NgxBaseComponentDirective}
|
|
5148
5590
|
* @implements {OnInit}
|
|
5149
5591
|
*/
|
|
5150
|
-
let EmptyStateComponent = class EmptyStateComponent extends
|
|
5592
|
+
let EmptyStateComponent = class EmptyStateComponent extends NgxComponentDirective {
|
|
5151
5593
|
/**
|
|
5152
5594
|
* @description Creates an instance of EmptyStateComponent.
|
|
5153
5595
|
* @summary Initializes a new EmptyStateComponent by calling the parent class constructor
|
|
@@ -5599,7 +6041,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
|
|
|
5599
6041
|
* @memberOf FieldsetComponent
|
|
5600
6042
|
*/
|
|
5601
6043
|
this.max = undefined;
|
|
5602
|
-
addIcons({ alertCircleOutline, createOutline });
|
|
6044
|
+
addIcons({ alertCircleOutline, trashOutline, createOutline });
|
|
5603
6045
|
}
|
|
5604
6046
|
/**
|
|
5605
6047
|
* @description Component initialization lifecycle method.
|
|
@@ -5729,16 +6171,16 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
|
|
|
5729
6171
|
event.stopImmediatePropagation();
|
|
5730
6172
|
const action = this.updatingItem ? OperationKeys.UPDATE : OperationKeys.CREATE;
|
|
5731
6173
|
const formGroup = this.activeFormGroup;
|
|
5732
|
-
const isValid =
|
|
6174
|
+
const isValid = NgxFormService.validateFields(formGroup);
|
|
5733
6175
|
// must pass correct pk here
|
|
5734
|
-
const isUnique =
|
|
6176
|
+
const isUnique = NgxFormService.isUniqueOnGroup(formGroup, action, action === OperationKeys.UPDATE ? this.updatingItem?.index : undefined);
|
|
5735
6177
|
const value = formGroup.value;
|
|
5736
6178
|
if (isValid) {
|
|
5737
6179
|
this.mapper = this.getMapper(value);
|
|
5738
6180
|
if (isUnique) {
|
|
5739
6181
|
this.isUniqueError = this.updatingItem = undefined;
|
|
5740
6182
|
this.setValue();
|
|
5741
|
-
|
|
6183
|
+
NgxFormService.addGroupToParent(formGroup.parent);
|
|
5742
6184
|
this.activeFormGroupIndex = formGroup.parent.length - 1;
|
|
5743
6185
|
this.getFormArrayIndex(this.activeFormGroupIndex);
|
|
5744
6186
|
}
|
|
@@ -6024,7 +6466,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
6024
6466
|
* @implements {OnInit}
|
|
6025
6467
|
* @memberOf SearchbarComponent
|
|
6026
6468
|
*/
|
|
6027
|
-
class SearchbarComponent extends
|
|
6469
|
+
class SearchbarComponent extends NgxComponentDirective {
|
|
6028
6470
|
/**
|
|
6029
6471
|
* @description Creates an instance of SearchbarComponent.
|
|
6030
6472
|
* @summary Initializes the SearchbarComponent with all necessary dependencies and configurations.
|
|
@@ -6535,7 +6977,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
6535
6977
|
*
|
|
6536
6978
|
* @memberOf ForAngularCommonModule
|
|
6537
6979
|
*/
|
|
6538
|
-
let FilterComponent = class FilterComponent extends
|
|
6980
|
+
let FilterComponent = class FilterComponent extends NgxComponentDirective {
|
|
6539
6981
|
/**
|
|
6540
6982
|
* @description Constructor for FilterComponent.
|
|
6541
6983
|
* @summary Initializes a new instance of the FilterComponent.
|
|
@@ -6709,7 +7151,7 @@ let FilterComponent = class FilterComponent extends NgxDecafComponentDirective {
|
|
|
6709
7151
|
* @memberOf FilterComponent
|
|
6710
7152
|
*/
|
|
6711
7153
|
this.searchEvent = new EventEmitter();
|
|
6712
|
-
addIcons({ chevronDownOutline, chevronUpOutline });
|
|
7154
|
+
addIcons({ chevronDownOutline, trashOutline, chevronUpOutline });
|
|
6713
7155
|
}
|
|
6714
7156
|
/**
|
|
6715
7157
|
* @description Initializes the component after Angular first displays the data-bound properties.
|
|
@@ -7229,7 +7671,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
7229
7671
|
* @extends {NgxBaseComponentDirective}
|
|
7230
7672
|
* @implements {OnInit}
|
|
7231
7673
|
*/
|
|
7232
|
-
class PaginationComponent extends
|
|
7674
|
+
class PaginationComponent extends NgxComponentDirective {
|
|
7233
7675
|
/**
|
|
7234
7676
|
* @constructor
|
|
7235
7677
|
* @description Initializes a new instance of the PaginationComponent.
|
|
@@ -7561,7 +8003,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
7561
8003
|
* @extends {NgxBaseComponentDirective}
|
|
7562
8004
|
* @implements {OnInit}
|
|
7563
8005
|
*/
|
|
7564
|
-
let ListComponent = class ListComponent extends
|
|
8006
|
+
let ListComponent = class ListComponent extends NgxComponentDirective {
|
|
7565
8007
|
/**
|
|
7566
8008
|
* @description Initializes a new instance of the ListComponent.
|
|
7567
8009
|
* @summary Creates a new ListComponent and sets up the base component with the appropriate
|
|
@@ -8719,7 +9161,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
8719
9161
|
* C->>C: Process action
|
|
8720
9162
|
* C->>V: Update view or navigate
|
|
8721
9163
|
*/
|
|
8722
|
-
let ListItemComponent = class ListItemComponent extends
|
|
9164
|
+
let ListItemComponent = class ListItemComponent extends NgxComponentDirective {
|
|
8723
9165
|
/**
|
|
8724
9166
|
* @description Creates an instance of ListItemComponent.
|
|
8725
9167
|
* @summary Initializes a new ListItemComponent by calling the parent class constructor
|
|
@@ -8795,17 +9237,6 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
|
|
|
8795
9237
|
* @memberOf ListItemComponent
|
|
8796
9238
|
*/
|
|
8797
9239
|
this.actionMenuOpen = false;
|
|
8798
|
-
/**
|
|
8799
|
-
* @description Angular NavController service for handling navigation.
|
|
8800
|
-
* @summary Injected service that provides methods for programmatic navigation
|
|
8801
|
-
* within the Ionic application. Used for navigating to different routes when
|
|
8802
|
-
* list item actions are performed or when the item itself is clicked.
|
|
8803
|
-
*
|
|
8804
|
-
* @private
|
|
8805
|
-
* @type {NavController}
|
|
8806
|
-
* @memberOf ListItemComponent
|
|
8807
|
-
*/
|
|
8808
|
-
this.navController = inject(NavController);
|
|
8809
9240
|
addIcons(allIcons);
|
|
8810
9241
|
}
|
|
8811
9242
|
/**
|
|
@@ -8860,7 +9291,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
|
|
|
8860
9291
|
* participant U as User
|
|
8861
9292
|
* participant L as ListItemComponent
|
|
8862
9293
|
* participant P as Parent Component
|
|
8863
|
-
* participant N as
|
|
9294
|
+
* participant N as Router
|
|
8864
9295
|
* participant E as Event System
|
|
8865
9296
|
*
|
|
8866
9297
|
* U->>L: Perform action (click/swipe)
|
|
@@ -8961,7 +9392,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
|
|
|
8961
9392
|
/**
|
|
8962
9393
|
* @description Navigates to a new route based on the specified action and item ID.
|
|
8963
9394
|
* @summary This method constructs a navigation URL using the component's route configuration,
|
|
8964
|
-
* the specified action, and an item identifier. It uses Ionic's
|
|
9395
|
+
* the specified action, and an item identifier. It uses Ionic's Router to perform
|
|
8965
9396
|
* forward navigation with appropriate animations. This method is typically used for
|
|
8966
9397
|
* CRUD operations where each action (create, read, update, delete) has its own route.
|
|
8967
9398
|
*
|
|
@@ -8972,7 +9403,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
|
|
|
8972
9403
|
* @mermaid
|
|
8973
9404
|
* sequenceDiagram
|
|
8974
9405
|
* participant L as ListItemComponent
|
|
8975
|
-
* participant N as
|
|
9406
|
+
* participant N as Router
|
|
8976
9407
|
* participant R as Router
|
|
8977
9408
|
*
|
|
8978
9409
|
* L->>L: redirect(action, id)
|
|
@@ -8985,7 +9416,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
|
|
|
8985
9416
|
* @memberOf ListItemComponent
|
|
8986
9417
|
*/
|
|
8987
9418
|
async redirect(action, id) {
|
|
8988
|
-
return await this.
|
|
9419
|
+
return await this.router.navigateByUrl(`/${this.route}/${action}/${id || this.uid}`);
|
|
8989
9420
|
}
|
|
8990
9421
|
/**
|
|
8991
9422
|
* @description Presents the actions menu popover for the list item.
|
|
@@ -9292,7 +9723,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
|
|
|
9292
9723
|
* @memberOf SteppedFormComponent
|
|
9293
9724
|
*/
|
|
9294
9725
|
handleNext(lastPage = false) {
|
|
9295
|
-
const isValid =
|
|
9726
|
+
const isValid = NgxFormService.validateFields(this.activeFormGroup);
|
|
9296
9727
|
if (!lastPage) {
|
|
9297
9728
|
if (isValid) {
|
|
9298
9729
|
this.activePage = this.activePage + 1;
|
|
@@ -9301,7 +9732,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
|
|
|
9301
9732
|
}
|
|
9302
9733
|
else {
|
|
9303
9734
|
if (isValid) {
|
|
9304
|
-
const data = Object.assign({}, ...Object.values(
|
|
9735
|
+
const data = Object.assign({}, ...Object.values(NgxFormService.getFormData(this.formGroup)));
|
|
9305
9736
|
this.submitEvent.emit({
|
|
9306
9737
|
data,
|
|
9307
9738
|
name: EventConstants.SUBMIT,
|
|
@@ -9556,5 +9987,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
|
|
|
9556
9987
|
* Generated bundle index. Do not edit.
|
|
9557
9988
|
*/
|
|
9558
9989
|
|
|
9559
|
-
export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, DefaultListEmptyOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent,
|
|
9990
|
+
export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, DefaultListEmptyOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, NgxComponentDirective, NgxEventHandler, NgxFormDirective, NgxFormFieldDirective, NgxFormService, NgxMediaDirective, NgxModelPageDirective, NgxPageDirective, NgxParentComponentDirective, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getModelRepository, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, provideDbAdapter, provideDynamicComponents, provideI18n, provideI18nLoader, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
|
|
9560
9991
|
//# sourceMappingURL=decaf-ts-for-angular.mjs.map
|