@smallpearl/ngx-helper 0.33.24 → 0.33.26

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.
@@ -1,5 +1,5 @@
1
1
  import * as i1 from '@angular/common/http';
2
- import { HttpContextToken, HttpParams, HttpContext } from '@angular/common/http';
2
+ import { HttpContextToken, HttpParams, HttpContext, HttpClient } from '@angular/common/http';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, inject, input, computed, signal, viewChild, ViewContainerRef, Component, ChangeDetectionStrategy, viewChildren, EventEmitter, effect, ContentChildren, Output, ChangeDetectorRef } from '@angular/core';
5
5
  import * as i4 from '@angular/common';
@@ -31,6 +31,7 @@ import { Observable, of, Subscription, tap, switchMap, firstValueFrom, map, catc
31
31
  import * as i1$1 from '@angular/material/toolbar';
32
32
  import { MatToolbarModule } from '@angular/material/toolbar';
33
33
  import { setServerErrorsAsFormErrors } from '@smallpearl/ngx-helper/forms';
34
+ import { sideloadToComposite } from '@smallpearl/ngx-helper/sideload';
34
35
 
35
36
  const SP_MAT_ENTITY_CRUD_HTTP_CONTEXT = new HttpContextToken(() => ({
36
37
  entityName: '',
@@ -120,6 +121,16 @@ class FormViewHostComponent {
120
121
  this.params.set(params);
121
122
  this.createClientView();
122
123
  }
124
+ // BEGIN SPMatEntityCrudCreateEditBridge METHODS //
125
+ getEntityName() {
126
+ return this.entityCrudComponentBase().getEntityName();
127
+ }
128
+ getIdKey() {
129
+ return this.entityCrudComponentBase().getIdKey();
130
+ }
131
+ getEntityUrl(entityId) {
132
+ return this.entityCrudComponentBase().getEntityUrl(entityId);
133
+ }
123
134
  close(cancel) {
124
135
  this.entityCrudComponentBase().closeCreateEdit(cancel);
125
136
  // destroy the client's form component
@@ -150,6 +161,10 @@ class FormViewHostComponent {
150
161
  ?.update(id, entityValue)
151
162
  .pipe(tap(() => this.close(false)));
152
163
  }
164
+ loadEntity(id, params) {
165
+ return this.entityCrudComponentBase().loadEntity(id, params);
166
+ }
167
+ // END SPMatEntityCrudCreateEditBridge METHODS //
153
168
  /**
154
169
  * Creates the client view provided via template
155
170
  */
@@ -184,14 +199,30 @@ class FormViewHostComponent {
184
199
  }
185
200
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: FormViewHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
186
201
  /** @nocollapse */ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: FormViewHostComponent, isStandalone: true, selector: "sp-create-edit-entity-host", inputs: { entityCrudComponentBase: { classPropertyName: "entityCrudComponentBase", publicName: "entityCrudComponentBase", isSignal: true, isRequired: true, transformFunction: null }, clientViewTemplate: { classPropertyName: "clientViewTemplate", publicName: "clientViewTemplate", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "vc", first: true, predicate: ["clientFormContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
187
- <div [class]="'sp-mat-crud-form-wrapper ' + entityCrudComponentBase().getFormPaneWrapperClass()" spHostBusyWheel="formBusyWheel" *transloco="let t">
188
- <div [class]="'sp-mat-crud-form-content ' + entityCrudComponentBase().getFormPaneContentClass()">
202
+ <div
203
+ [class]="
204
+ 'sp-mat-crud-form-wrapper ' +
205
+ entityCrudComponentBase().getFormPaneWrapperClass()
206
+ "
207
+ spHostBusyWheel="formBusyWheel"
208
+ *transloco="let t"
209
+ >
210
+ <div
211
+ [class]="
212
+ 'sp-mat-crud-form-content ' +
213
+ entityCrudComponentBase().getFormPaneContentClass()
214
+ "
215
+ >
189
216
  <div class="create-edit-topbar">
190
217
  <div class="title">
191
218
  @if (title()) {
192
- {{ title() | async }}
219
+ {{ title() | async }}
193
220
  } @else {
194
- {{ t(('spMatEntityCrud.' + (entity() ? 'editItem' : 'newItem')), { item: (this._itemLabel() | async )}) }}
221
+ {{
222
+ t('spMatEntityCrud.' + (entity() ? 'editItem' : 'newItem'), {
223
+ item: (this._itemLabel() | async)
224
+ })
225
+ }}
195
226
  }
196
227
  </div>
197
228
  <div class="spacer"></div>
@@ -217,14 +248,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
217
248
  TranslocoModule,
218
249
  SPMatHostBusyWheelDirective,
219
250
  ], selector: 'sp-create-edit-entity-host', template: `
220
- <div [class]="'sp-mat-crud-form-wrapper ' + entityCrudComponentBase().getFormPaneWrapperClass()" spHostBusyWheel="formBusyWheel" *transloco="let t">
221
- <div [class]="'sp-mat-crud-form-content ' + entityCrudComponentBase().getFormPaneContentClass()">
251
+ <div
252
+ [class]="
253
+ 'sp-mat-crud-form-wrapper ' +
254
+ entityCrudComponentBase().getFormPaneWrapperClass()
255
+ "
256
+ spHostBusyWheel="formBusyWheel"
257
+ *transloco="let t"
258
+ >
259
+ <div
260
+ [class]="
261
+ 'sp-mat-crud-form-content ' +
262
+ entityCrudComponentBase().getFormPaneContentClass()
263
+ "
264
+ >
222
265
  <div class="create-edit-topbar">
223
266
  <div class="title">
224
267
  @if (title()) {
225
- {{ title() | async }}
268
+ {{ title() | async }}
226
269
  } @else {
227
- {{ t(('spMatEntityCrud.' + (entity() ? 'editItem' : 'newItem')), { item: (this._itemLabel() | async )}) }}
270
+ {{
271
+ t('spMatEntityCrud.' + (entity() ? 'editItem' : 'newItem'), {
272
+ item: (this._itemLabel() | async)
273
+ })
274
+ }}
228
275
  }
229
276
  </div>
230
277
  <div class="spacer"></div>
@@ -662,6 +709,16 @@ class SPMatEntityCrudComponent extends SPMatEntityListComponent {
662
709
  refresh(force = false) {
663
710
  this.spEntitiesList()?.refresh(force);
664
711
  }
712
+ // BEGIN SPMatEntityCrudComponentBase METHODS //
713
+ getEntityName() {
714
+ return this.entityName();
715
+ }
716
+ getEntityNamePlural() {
717
+ return this._entityNamePlural();
718
+ }
719
+ getIdKey() {
720
+ return this.idKey();
721
+ }
665
722
  closeCreateEdit(cancelled) {
666
723
  this.createEditViewActive.set(false);
667
724
  this.entityViewPaneActivated.emit({
@@ -733,6 +790,22 @@ class SPMatEntityCrudComponent extends SPMatEntityListComponent {
733
790
  }
734
791
  }));
735
792
  }
793
+ loadEntity(id, params) {
794
+ const crudOpFn = this.crudOpFn();
795
+ if (crudOpFn) {
796
+ return crudOpFn('get', id, undefined, this);
797
+ }
798
+ else {
799
+ const httpParams = params instanceof HttpParams
800
+ ? params
801
+ : new HttpParams({ fromString: params });
802
+ return this.http.get(this.getEntityUrl(id), {
803
+ context: this.getCrudReqHttpContext('retrieve'),
804
+ params: httpParams,
805
+ });
806
+ }
807
+ }
808
+ // END SPMatEntityCrudComponentBase METHODS //
736
809
  /**
737
810
  * Thunk these methods to the internal <sp-mat-entity-list> component.
738
811
  */
@@ -834,10 +907,11 @@ class SPMatEntityCrudComponent extends SPMatEntityListComponent {
834
907
  return;
835
908
  }
836
909
  }
837
- this.doEntityAction(entity[this.idKey()], verb, httpRequestParameters.params || new HttpParams(), httpRequestParameters.body).pipe(tap((response) => {
910
+ this.doEntityAction(entity[this.idKey()], verb, httpRequestParameters.params || new HttpParams(), httpRequestParameters.body)
911
+ .pipe(tap((response) => {
838
912
  const successMessage = actionItem?.successMessage ||
839
913
  this.transloco.translate('spMatEntityCrud.done');
840
- this.snackBar.open(successMessage || "Done");
914
+ this.snackBar.open(successMessage || 'Done');
841
915
  }), catchError((error) => {
842
916
  /**
843
917
  * If an errorMessage is specified in the actionItem, display it.
@@ -849,7 +923,8 @@ class SPMatEntityCrudComponent extends SPMatEntityListComponent {
849
923
  return EMPTY;
850
924
  }
851
925
  return throwError(() => error);
852
- })).subscribe();
926
+ }))
927
+ .subscribe();
853
928
  }
854
929
  onCreate(event) {
855
930
  // If newItemLink() has not been provided, check if createEditFormTemplate
@@ -1053,7 +1128,8 @@ class SPMatEntityCrudComponent extends SPMatEntityListComponent {
1053
1128
  // be an array of HttpContextToken key, value pairs.
1054
1129
  contextParamToHttpContext(context, crudHttpReqContext);
1055
1130
  }
1056
- else if (typeof crudHttpReqContext === 'object' && op &&
1131
+ else if (typeof crudHttpReqContext === 'object' &&
1132
+ op &&
1057
1133
  Object.keys(crudHttpReqContext).find((k) => k === op)) {
1058
1134
  // HttpContext specific to this crud operation, 'create'|'retrieve'|'update'|'delete'
1059
1135
  contextParamToHttpContext(context, crudHttpReqContext[op]);
@@ -1678,22 +1754,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
1678
1754
  * ```
1679
1755
  *
1680
1756
  * 3. If you form's value requires manipulation before being sent to the
1681
- * server, override getFormValue() method and do it there before returning
1757
+ * server, override `getFormValue()` method and do it there before returning
1682
1758
  * the modified values.
1683
1759
  *
1684
1760
  * 4. Wire up the form in the template as:
1685
1761
  *
1686
1762
  * ```
1763
+ * @if (loadEntity$ | async) {
1687
1764
  * <form [formGroup]='form'.. (ngSubmit)="onSubmit()">
1688
1765
  * <button type="submit">Submit</button>
1689
1766
  * </form>
1767
+ * } @else {
1768
+ * <div>Loading...</div>
1769
+ * }
1690
1770
  * ```
1771
+ * Here `loadEntity$` is an Observable<boolean> that upon emission of `true`
1772
+ * indicates that the entity has been loaded from server (in case of edit)
1773
+ * and the form is ready to be displayed. Note that if the full entity was
1774
+ * passed in the `entity` input property, then no server load is necessary
1775
+ * and the form will be created immediately.
1776
+ *
1777
+ * 5. If the entity shape required by the form requires additional parameters
1778
+ * to be loaded from server, initialize `entity` property with it's id.
1779
+ * Then override the `getLoadEntityParams()` method to return the additional
1780
+ * load parameters. The parameters returned by this method will be
1781
+ * passed to the `loadEntity()` method of the bridge interface.
1691
1782
  */
1692
1783
  class SPMatEntityCrudFormBase {
1693
1784
  _form = signal(undefined);
1694
1785
  entity = input.required();
1695
1786
  bridge = input.required();
1696
1787
  params = input();
1788
+ loadEntity$;
1789
+ _entity = signal(undefined);
1697
1790
  sub$ = new Subscription();
1698
1791
  // Force typecast to TFormGroup so that we can use it in the template
1699
1792
  // without having to use the non-nullable operator ! with every reference
@@ -1701,9 +1794,10 @@ class SPMatEntityCrudFormBase {
1701
1794
  // method after the form is created. And if form() is not set, then there
1702
1795
  // will be errors while loading the form in the template.
1703
1796
  form = computed(() => this._form());
1704
- crudConfig = getEntityCrudConfig();
1797
+ // crudConfig = getEntityCrudConfig();
1705
1798
  transloco = inject(TranslocoService);
1706
1799
  cdr = inject(ChangeDetectorRef);
1800
+ http = inject(HttpClient);
1707
1801
  canCancelEdit = () => {
1708
1802
  return this._canCancelEdit();
1709
1803
  };
@@ -1715,12 +1809,50 @@ class SPMatEntityCrudFormBase {
1715
1809
  return true;
1716
1810
  }
1717
1811
  ngOnInit() {
1718
- this._form.set(this.createForm(this.entity()));
1719
- this.bridge()?.registerCanCancelEditCallback(this.canCancelEdit);
1812
+ this.loadEntity$ = (typeof this.entity() === 'object' || this.entity() === undefined
1813
+ ? new Observable((subscriber) => {
1814
+ subscriber.next(this.entity());
1815
+ subscriber.complete();
1816
+ })
1817
+ : this.bridge()?.loadEntity(this.entity(), this.getLoadEntityParams())).pipe(map((resp) => {
1818
+ const compositeEntity = this.getEntityFromLoadResponse(resp);
1819
+ this._entity.set(compositeEntity);
1820
+ this._form.set(this.createForm(compositeEntity));
1821
+ this.bridge()?.registerCanCancelEditCallback(this.canCancelEdit);
1822
+ return true;
1823
+ }));
1720
1824
  }
1721
1825
  ngOnDestroy() {
1722
1826
  this.sub$.unsubscribe();
1723
1827
  }
1828
+ /**
1829
+ * Additional parameters for loading the entity, in case this.entity() value
1830
+ * is of type TEntity[IdKey].
1831
+ * @returns
1832
+ */
1833
+ getLoadEntityParams() {
1834
+ return '';
1835
+ }
1836
+ /**
1837
+ * Return the TEntity object from the response returned by the
1838
+ * loadEntity() method of the bridge. Typically entity load return the actual
1839
+ * entity object itself. In some cases, where response is sideloaded, the
1840
+ * default implementation here uses the `sideloadToComposite()` utility to
1841
+ * extract the entity from the response after merging (inplace) the
1842
+ * sideloaded data into a composite.
1843
+ *
1844
+ * If you have a different response shape, override this method to
1845
+ * extract the TEntity object from the response.
1846
+ * @param resp
1847
+ * @returns
1848
+ */
1849
+ getEntityFromLoadResponse(resp) {
1850
+ if (!resp) {
1851
+ return undefined;
1852
+ }
1853
+ const sideloaded = sideloadToComposite(resp, this.bridge().getEntityName(), this.bridge().getIdKey());
1854
+ return sideloaded;
1855
+ }
1724
1856
  /**
1725
1857
  * Override to customize the id key name if it's not 'id'
1726
1858
  * @returns The name of the unique identifier key that will be used to
@@ -1753,8 +1885,9 @@ class SPMatEntityCrudFormBase {
1753
1885
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SPMatEntityCrudFormBase, decorators: [{
1754
1886
  type: Component,
1755
1887
  args: [{
1756
- selector: '_#_sp-mat-entity-crud-form-base_#_', template: ``,
1757
- standalone: false
1888
+ selector: '_#_sp-mat-entity-crud-form-base_#_',
1889
+ template: ``,
1890
+ standalone: false,
1758
1891
  }]
1759
1892
  }] });
1760
1893