@decaf-ts/for-angular 0.0.73 → 0.0.76

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.
@@ -12,7 +12,7 @@ import { TranslateModule, TranslatePipe, TranslateParser, provideTranslateServic
12
12
  import { Logging, LoggedClass } from '@decaf-ts/logging';
13
13
  import { Repository, OrderDirection, Condition } from '@decaf-ts/core';
14
14
  import { uses, Metadata, apply, metadata } from '@decaf-ts/decoration';
15
- import { AnimationController, provideIonicAngular, IonIcon, IonButton, IonModal, IonSpinner, IonButtons, IonContent, IonHeader, IonTitle, IonToolbar, LoadingController, IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonBadge, IonText, IonTextarea, IonCard, IonCardHeader, IonCardContent, IonCardTitle, IonCardSubtitle, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonPopover, NavController } from '@ionic/angular/standalone';
15
+ import { AnimationController, provideIonicAngular, LoadingController, IonIcon, IonButton, IonModal, IonSpinner, IonButtons, IonContent, IonHeader, IonTitle, IonToolbar, IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonBadge, IonText, IonTextarea, IonCard, IonCardHeader, IonCardContent, IonCardTitle, IonCardSubtitle, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonPopover, NavController } from '@ionic/angular/standalone';
16
16
  import { faker } from '@faker-js/faker';
17
17
  import { Router, NavigationStart, ActivatedRoute, NavigationEnd } from '@angular/router';
18
18
  import { forkJoin, Subject, BehaviorSubject, fromEvent, of, merge, Observable, timer, firstValueFrom, debounceTime, takeUntil as takeUntil$1, shareReplay as shareReplay$1 } from 'rxjs';
@@ -65,6 +65,10 @@ const ActionRoles = {
65
65
  submit: 'submit',
66
66
  clear: 'clear',
67
67
  back: 'back',
68
+ [OperationKeys.CREATE]: OperationKeys.CREATE,
69
+ [OperationKeys.READ]: OperationKeys.READ,
70
+ [OperationKeys.UPDATE]: OperationKeys.UPDATE,
71
+ [OperationKeys.DELETE]: OperationKeys.DELETE,
68
72
  };
69
73
  const WindowColorSchemes = {
70
74
  light: 'light',
@@ -3908,6 +3912,17 @@ class NgxComponentDirective extends DecafComponent {
3908
3912
  * @type {Location}
3909
3913
  */
3910
3914
  this.location = inject(Location);
3915
+ /**
3916
+ * @description Ionic loading overlay manager available to derived components.
3917
+ * @summary Provides convenient access to Ionic's LoadingController, enabling directive
3918
+ * subclasses to create, present, and dismiss loading overlays without wiring the
3919
+ * service themselves. Use this helper to surface async progress indicators tied to
3920
+ * CRUD operations, navigation, or any long-running UI task.
3921
+ * @type {LoadingController}
3922
+ * @returns {OverlayBaseController<LoadingOptions, HTMLIonLoadingElement>}
3923
+ * @memberOf module:lib/engine/NgxComponentDirective
3924
+ */
3925
+ this.loadingController = inject(LoadingController);
3911
3926
  /**
3912
3927
  * @description Flag indicating if the component is rendered as a child of a modal dialog.
3913
3928
  * @summary Determines whether this component instance is being displayed within a modal
@@ -4122,9 +4137,9 @@ class NgxComponentDirective extends DecafComponent {
4122
4137
  * @memberOf ListComponent
4123
4138
  */
4124
4139
  async handleRepositoryRefresh(...args) {
4125
- const [table, event, uid] = args;
4126
- if (event === OperationKeys.CREATE && !!uid)
4127
- return this.handleObserveEvent(table, event, uid);
4140
+ const [modelInstance, event, uid] = args;
4141
+ if ([OperationKeys.CREATE].includes(event))
4142
+ return this.handleObserveEvent(modelInstance, event, uid);
4128
4143
  return this.repositoryObserverSubject.next(args);
4129
4144
  }
4130
4145
  async handleObserveEvent(...args) {
@@ -5230,6 +5245,9 @@ class ModelRendererComponent extends NgxRenderableComponentDirective {
5230
5245
  if (this.output?.inputs)
5231
5246
  this.rendererId = sf(AngularEngineKeys.RENDERED_ID, this.output.inputs['rendererId']);
5232
5247
  this.instance = this.output?.component;
5248
+ const { operation } = this.globals || {};
5249
+ if (operation)
5250
+ this.operation = operation;
5233
5251
  this.subscribeEvents();
5234
5252
  }
5235
5253
  }
@@ -6091,7 +6109,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
6091
6109
  * @memberOf CrudFieldComponent
6092
6110
  */
6093
6111
  this.translatable = true;
6094
- this.loadingController = inject(LoadingController);
6095
6112
  addIcons({ chevronDownOutline, chevronUpOutline });
6096
6113
  }
6097
6114
  /**
@@ -6105,7 +6122,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
6105
6122
  * @memberOf CrudFieldComponent
6106
6123
  */
6107
6124
  async ngOnInit() {
6108
- this.options = await this.getOptions();
6109
6125
  if (Array.isArray(this.hidden) && !this.hidden.includes(this.operation))
6110
6126
  this.hidden = false;
6111
6127
  if (this.readonly && typeof this.readonly === Function.name.toLocaleLowerCase())
@@ -6117,6 +6133,7 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
6117
6133
  this.formGroup = undefined;
6118
6134
  }
6119
6135
  else {
6136
+ this.options = await this.getOptions();
6120
6137
  if (!this.parentForm && this.formGroup instanceof FormGroup || this.formGroup instanceof FormArray)
6121
6138
  this.parentForm = (this.formGroup.root || this.formControl.root);
6122
6139
  if (this.multiple) {
@@ -6147,11 +6164,11 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
6147
6164
  return [];
6148
6165
  if (this.options instanceof Function) {
6149
6166
  if (this.options.name === 'options')
6150
- this.options = this.options();
6167
+ this.options = await this.options();
6151
6168
  const fnName = this.options?.name;
6152
6169
  if (fnName) {
6153
6170
  if (fnName === 'function') {
6154
- this.options = this.options();
6171
+ this.options = await this.options();
6155
6172
  }
6156
6173
  else {
6157
6174
  const repo = getModelAndRepository(this.options?.['name'], this);
@@ -6580,14 +6597,8 @@ class NgxFormDirective extends NgxParentComponentDirective {
6580
6597
  * @returns {void}
6581
6598
  */
6582
6599
  handleReset() {
6583
- if (this.isModalChild) {
6584
- this.submitEvent.emit({
6585
- data: null,
6586
- component: this.componentName,
6587
- name: ActionRoles.cancel,
6588
- });
6589
- return;
6590
- }
6600
+ if (this.isModalChild)
6601
+ return this.submitEventEmit(null, ActionRoles.cancel, this.componentName);
6591
6602
  if (![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation) && this.allowClear)
6592
6603
  return NgxFormService.reset(this.formGroup);
6593
6604
  this.location.back();
@@ -6605,14 +6616,17 @@ class NgxFormDirective extends NgxParentComponentDirective {
6605
6616
  return false;
6606
6617
  }
6607
6618
  const data = NgxFormService.getFormData(this.formGroup);
6608
- if (Object.keys(data).length > 0) {
6609
- this.submitEvent.emit({
6610
- data,
6611
- component: componentName || this.componentName,
6612
- name: eventName || this.action || ComponentEventNames.SUBMIT,
6613
- handlers: this.handlers,
6614
- });
6615
- }
6619
+ if (Object.keys(data).length > 0)
6620
+ return this.submitEventEmit(data, eventName, componentName, this.handlers);
6621
+ }
6622
+ submitEventEmit(data, componentName, eventName, handlers) {
6623
+ this.submitEvent.emit({
6624
+ data,
6625
+ component: componentName || this.componentName,
6626
+ name: eventName || ComponentEventNames.SUBMIT,
6627
+ role: this.operation,
6628
+ handlers: { ...(this.handlers || {}), ...(handlers || {}) },
6629
+ });
6616
6630
  }
6617
6631
  /**
6618
6632
  * @description Updates the active form group and children for the specified page.
@@ -7110,14 +7124,10 @@ let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
7110
7124
  * @memberOf CrudFormComponent
7111
7125
  */
7112
7126
  handleDelete() {
7113
- this.submitEvent.emit({
7114
- data: this.model,
7115
- component: 'CrudFormComponent',
7116
- name: ComponentEventNames.SUBMIT,
7117
- });
7127
+ super.submitEventEmit(this.model, ComponentEventNames.SUBMIT, 'CrudFormComponent', this.handlers);
7118
7128
  }
7119
7129
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CrudFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7120
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", host: { properties: { "attr.id": "uid" } }, usesInheritance: true, ngImport: i0, template: "@if (operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (ngSubmit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if (!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [className]=\"'dcf-crud-form-grid dcf-grid-nested '\"\n [children]=\"children || []\"\n [parentForm]=\"formGroup || parentForm\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n }\n @if (initialized) {\n <div class=\"dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n @if (!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ locale + '.' + (allowClear ? options.buttons.clear?.text : 'back') | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n\n </div>\n }\n\n </form>\n} @else {\n <section #component [id]=\"uid\">\n <ngx-decaf-layout\n [className]=\"''\"\n [children]=\"children || []\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n </section>\n\n <section [class]=\"'dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if (operations.includes(OperationKeys.DELETE) && [OperationKeys.READ, OperationKeys.DELETE].includes(operation) && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{locale + '.delete' | translate}}\n </ion-button>\n </div>\n }\n @if (operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <div>\n <ion-button\n type=\"submit\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? (locale + '.back' | translate) : options.buttons.clear?.text}}\n </ion-button>\n </div>\n </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex.read,.dcf-buttons-grid.dcf-flex.delete{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex{flex-direction:column-reverse}.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid.create,.dcf-form-grid.update{margin-top:var(--dcf-margin-small)}.dcf-form-grid .dcf-form-item{margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:var(--dcf-margin-small)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "grid", "flexMode", "rowCard", "maxColsLength"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
7130
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", host: { properties: { "attr.id": "uid" } }, usesInheritance: true, ngImport: i0, template: "@if (operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (ngSubmit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if (!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [className]=\"'dcf-crud-form-grid dcf-grid-nested '\"\n [children]=\"children || []\"\n [parentForm]=\"formGroup || parentForm\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n }\n @if (initialized) {\n <div class=\"dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n @if (!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ locale + '.' + (allowClear ? options.buttons.clear?.text : 'back') | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n\n </div>\n }\n\n </form>\n} @else {\n <section #component [id]=\"uid\">\n <ngx-decaf-layout\n [className]=\"''\"\n [children]=\"children || []\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n </section>\n\n <section [class]=\"'dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if ([OperationKeys.READ, OperationKeys.DELETE].includes(operation) && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{locale + '.delete' | translate}}\n </ion-button>\n </div>\n }\n @if (operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <div>\n <ion-button\n type=\"submit\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? (locale + '.back' | translate) : options.buttons.clear?.text}}\n </ion-button>\n </div>\n </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex.read,.dcf-buttons-grid.dcf-flex.delete{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex{flex-direction:column-reverse}.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid.create,.dcf-form-grid.update{margin-top:var(--dcf-margin-small)}.dcf-form-grid .dcf-form-item{margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:var(--dcf-margin-small)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "grid", "flexMode", "rowCard", "maxColsLength"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
7121
7131
  };
7122
7132
  CrudFormComponent = __decorate([
7123
7133
  Dynamic(),
@@ -7125,7 +7135,7 @@ CrudFormComponent = __decorate([
7125
7135
  ], CrudFormComponent);
7126
7136
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CrudFormComponent, decorators: [{
7127
7137
  type: Component,
7128
- args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [TranslatePipe, ReactiveFormsModule, LayoutComponent, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if (operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (ngSubmit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if (!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [className]=\"'dcf-crud-form-grid dcf-grid-nested '\"\n [children]=\"children || []\"\n [parentForm]=\"formGroup || parentForm\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n }\n @if (initialized) {\n <div class=\"dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n @if (!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ locale + '.' + (allowClear ? options.buttons.clear?.text : 'back') | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n\n </div>\n }\n\n </form>\n} @else {\n <section #component [id]=\"uid\">\n <ngx-decaf-layout\n [className]=\"''\"\n [children]=\"children || []\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n </section>\n\n <section [class]=\"'dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if (operations.includes(OperationKeys.DELETE) && [OperationKeys.READ, OperationKeys.DELETE].includes(operation) && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{locale + '.delete' | translate}}\n </ion-button>\n </div>\n }\n @if (operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <div>\n <ion-button\n type=\"submit\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? (locale + '.back' | translate) : options.buttons.clear?.text}}\n </ion-button>\n </div>\n </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex.read,.dcf-buttons-grid.dcf-flex.delete{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex{flex-direction:column-reverse}.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid.create,.dcf-form-grid.update{margin-top:var(--dcf-margin-small)}.dcf-form-grid .dcf-form-item{margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:var(--dcf-margin-small)}\n"] }]
7138
+ args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [TranslatePipe, ReactiveFormsModule, LayoutComponent, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if (operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (ngSubmit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if (!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [className]=\"'dcf-crud-form-grid dcf-grid-nested '\"\n [children]=\"children || []\"\n [parentForm]=\"formGroup || parentForm\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n }\n @if (initialized) {\n <div class=\"dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n @if (!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ locale + '.' + (allowClear ? options.buttons.clear?.text : 'back') | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n\n </div>\n }\n\n </form>\n} @else {\n <section #component [id]=\"uid\">\n <ngx-decaf-layout\n [className]=\"''\"\n [children]=\"children || []\"\n [operation]=\"operation\"\n [cardType]=\"cardType ?? 'blank'\"\n [match]=\"match ?? false\"\n [gap]=\"'small'\"\n [flexMode]=\"props.flexMode || false\"\n [rows]=\"rows\"\n [borders]=\"borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [cols]=\"cols\" />\n </section>\n\n <section [class]=\"'dcf-buttons-grid dcf-grid dcf-grid-small dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if ([OperationKeys.READ, OperationKeys.DELETE].includes(operation) && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{locale + '.delete' | translate}}\n </ion-button>\n </div>\n }\n @if (operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n <div>\n <ion-button\n type=\"submit\">\n @if (options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : (locale + '.' + options.buttons.submit.text) | translate }}\n </ion-button>\n </div>\n }\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if (options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? (locale + '.back' | translate) : options.buttons.clear?.text}}\n </ion-button>\n </div>\n </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex.read,.dcf-buttons-grid.dcf-flex.delete{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex{flex-direction:column-reverse}.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid.create,.dcf-form-grid.update{margin-top:var(--dcf-margin-small)}.dcf-form-grid .dcf-form-item{margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:var(--dcf-margin-small)}\n"] }]
7129
7139
  }], ctorParameters: () => [] });
7130
7140
 
7131
7141
  /**
@@ -10073,7 +10083,7 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10073
10083
  .subscribe((event) => this.clickEventEmit(event));
10074
10084
  this.repositoryObserverSubject
10075
10085
  .pipe(debounceTime(100), shareReplay$1(1), takeUntil$1(this.destroySubscriptions$))
10076
- .subscribe(([table, event, uid]) => this.handleObserveEvent(table, event, uid));
10086
+ .subscribe(([modelInstance, event, uid]) => this.handleObserveEvent(modelInstance, event, uid));
10077
10087
  this.limit = Number(this.limit);
10078
10088
  this.start = Number(this.start);
10079
10089
  this.enableFilter = stringToBoolean(this.enableFilter);
@@ -10143,14 +10153,23 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10143
10153
  * @returns {KeyValue} A mapper object that contains string values mapped to the
10144
10154
  * component's public keys.
10145
10155
  */
10146
- get _mapper() {
10156
+ async getMapper() {
10147
10157
  this.mapper = { ...this.mapper, ...{ uid: this.pk } };
10148
- return Object.keys(this.mapper).reduce((accum, curr) => {
10149
- const mapper = this.mapper[curr];
10150
- if (typeof mapper === 'string')
10151
- accum[curr] = mapper;
10152
- return accum;
10153
- }, {});
10158
+ const mapper = {};
10159
+ for (const [key, value] of Object.entries(this.mapper)) {
10160
+ if (typeof value === Primitives.STRING) {
10161
+ mapper[key] = value;
10162
+ continue;
10163
+ }
10164
+ const mapperFn = value?.['valueParserFn'] || undefined;
10165
+ if (typeof mapperFn === 'function') {
10166
+ const value = await mapperFn(key, this);
10167
+ if (typeof value === Primitives.STRING) {
10168
+ mapper[value] = key;
10169
+ }
10170
+ }
10171
+ }
10172
+ return mapper;
10154
10173
  }
10155
10174
  /**
10156
10175
  * @description Refreshes the list data from the configured source.
@@ -10246,13 +10265,13 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10246
10265
  * the appropriate list operations. This includes adding new items, updating existing
10247
10266
  * ones, or removing deleted items from the list display.
10248
10267
  *
10249
- * @param {string} table - The table/model name that changed
10268
+ * @param {UIFunctionLike} clazz - The model instance that changed
10250
10269
  * @param {OperationKeys} event - The type of operation (CREATE, UPDATE, DELETE)
10251
10270
  * @param {string | number} uid - The unique identifier of the affected item
10252
10271
  * @returns {Promise<void>}
10253
10272
  * @memberOf ListComponent
10254
10273
  */
10255
- async handleObserveEvent(table, event, uid) {
10274
+ async handleObserveEvent(clazz, event, uid) {
10256
10275
  if (event === OperationKeys.CREATE) {
10257
10276
  if (uid) {
10258
10277
  await this.handleCreate(uid);
@@ -10296,7 +10315,7 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10296
10315
  */
10297
10316
  async handleCreate(uid) {
10298
10317
  const result = await this._repository?.read(uid);
10299
- const item = this.mapResults([result])[0];
10318
+ const item = (await this.mapResults([result]))[0];
10300
10319
  this.items = this.data = [item, ...(this.items || [])];
10301
10320
  }
10302
10321
  /**
@@ -10512,8 +10531,7 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10512
10531
  */
10513
10532
  parseSearchResults(results, search) {
10514
10533
  const filtered = results.filter((item) => Object.values(item).some((v) => {
10515
- if (v
10516
- .toString()
10534
+ if (`${v}`
10517
10535
  .toLowerCase()
10518
10536
  .includes(search?.toLowerCase()))
10519
10537
  return v;
@@ -10596,7 +10614,11 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10596
10614
  async getFromModel(force = false) {
10597
10615
  let data = [...(this.data || [])];
10598
10616
  let request = [];
10599
- // getting model repository
10617
+ if (!this.repositoryObserver) {
10618
+ this.repositoryObserver = {
10619
+ refresh: async (...args) => this.handleRepositoryRefresh(...args),
10620
+ };
10621
+ }
10600
10622
  if (!this._repository) {
10601
10623
  this._repository = this.repository;
10602
10624
  try {
@@ -10783,7 +10805,7 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10783
10805
  this.getMoreData(result?.length || 0);
10784
10806
  }
10785
10807
  return Object.keys(this.mapper || {}).length
10786
- ? this.mapResults(result)
10808
+ ? await this.mapResults(result)
10787
10809
  : result;
10788
10810
  }
10789
10811
  /**
@@ -10871,11 +10893,10 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
10871
10893
  *
10872
10894
  * @memberOf ListComponent
10873
10895
  */
10874
- mapResults(data) {
10896
+ async mapResults(data) {
10875
10897
  if (!data || !data.length)
10876
10898
  return [];
10877
- // passing uid as prop to mapper
10878
- this.mapper = this._mapper;
10899
+ this.mapper = await this.getMapper();
10879
10900
  const props = Object.assign({
10880
10901
  operations: this.operations,
10881
10902
  route: this.route,
@@ -11621,12 +11642,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxFormDirective {
11621
11642
  if (isValid) {
11622
11643
  const rootForm = this.formGroup?.root || this.formGroup;
11623
11644
  const data = Object.assign({}, ...Object.values(NgxFormService.getFormData(rootForm)));
11624
- this.submitEvent.emit({
11625
- data,
11626
- component: this.componentName,
11627
- name: this.action || ComponentEventNames.SUBMIT,
11628
- handlers: this.handlers,
11629
- });
11645
+ super.submitEventEmit(data, this.action || ComponentEventNames.SUBMIT, 'SteppedFormComponent', this.handlers);
11630
11646
  }
11631
11647
  }
11632
11648
  }
@@ -12649,6 +12665,9 @@ let TableComponent = class TableComponent extends ListComponent {
12649
12665
  }
12650
12666
  async ngOnInit() {
12651
12667
  this.initialized = false;
12668
+ this.repositoryObserver = {
12669
+ refresh: async (...args) => this.handleRepositoryRefresh(...args),
12670
+ };
12652
12671
  this.type = ListComponentsTypes.PAGINATED;
12653
12672
  this.empty = Object.assign({}, DefaultListEmptyOptions, this.empty);
12654
12673
  if (!this.initialized)
@@ -12724,7 +12743,7 @@ let TableComponent = class TableComponent extends ListComponent {
12724
12743
  return { ...accum, [curr]: parserFn ? parserFn(mapped[curr], this) : mapped[curr] };
12725
12744
  }, { ...props });
12726
12745
  }
12727
- mapResults(data) {
12746
+ async mapResults(data) {
12728
12747
  if (!data || !data.length)
12729
12748
  return [];
12730
12749
  return data.reduce((accum, curr) => [
@@ -13132,21 +13151,6 @@ class NgxModelPageDirective extends NgxPageDirective {
13132
13151
  * @memberOf ModelPage
13133
13152
  */
13134
13153
  this.operation = OperationKeys.READ;
13135
- /**
13136
- * @description Array of operations allowed for the current model instance.
13137
- * @summary Dynamically determined list of operations that are permitted based on
13138
- * the current context and model state. Initially contains CREATE and READ operations,
13139
- * with UPDATE and DELETE added when a modelId is present. This controls which
13140
- * action buttons are displayed and which operations are accessible to the user.
13141
- *
13142
- * @type {OperationKeys[]}
13143
- * @default [OperationKeys.CREATE, OperationKeys.READ]
13144
- * @memberOf ModelPage
13145
- */
13146
- this.allowedOperations = [
13147
- OperationKeys.CREATE,
13148
- OperationKeys.READ,
13149
- ];
13150
13154
  /**
13151
13155
  * @description Current model data loaded from the repository.
13152
13156
  * @summary Stores the raw data object representing the current model instance retrieved
@@ -13212,7 +13216,7 @@ class NgxModelPageDirective extends NgxPageDirective {
13212
13216
  async ionViewWillEnter() {
13213
13217
  // await super.ionViewWillEnter();
13214
13218
  if (this.modelId)
13215
- this.allowedOperations = this.allowedOperations.concat([
13219
+ this.operations = this.operations.concat([
13216
13220
  OperationKeys.UPDATE,
13217
13221
  OperationKeys.DELETE,
13218
13222
  ]);