ngx-dynamic-entity 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-dynamic-entity",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Angular standalone UI library for rendering dynamic entity forms.",
5
5
  "//typings": "Local workspace resolution only: ng-packagr 21 writes types/ngx-dynamic-entity.d.ts into dist/package.json. Do NOT add a 'main' field here — ng-packagr does not generate one, so it would be copied verbatim and resolve wrongly in the published package.",
6
6
  "typings": "types/ngx-dynamic-entity.d.ts",
@@ -8,7 +8,7 @@
8
8
  "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
9
9
  "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
10
10
  "@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
11
- "@dynamic-entity/core": "^1.9.0",
11
+ "@dynamic-entity/core": "^1.10.0",
12
12
  "rxjs": "^7.0.0"
13
13
  },
14
14
  "keywords": [
@@ -1252,9 +1252,22 @@ declare class DynamicRecordFormComponent implements OnChanges {
1252
1252
  tabId: string;
1253
1253
  record: Record<string, any>;
1254
1254
  }>;
1255
+ /**
1256
+ * A `beforeSave` hook aborted the save — by returning false or throwing.
1257
+ *
1258
+ * Re-exposed from the embedded `ngx-dynamic-form`, and emitted by `saveSection` too. The
1259
+ * inner form already ran the hook and already refused the save, but nothing carried that
1260
+ * out to a host binding this component: the veto worked and looked exactly like a Save
1261
+ * button that did nothing, which is the failure `saveRejected` exists to prevent.
1262
+ */
1263
+ saveRejected: EventEmitter<{
1264
+ reason: string;
1265
+ error?: unknown;
1266
+ }>;
1255
1267
  dynamicFormComp?: DynamicFormComponent;
1256
1268
  private readonly rulesEvaluation;
1257
1269
  private readonly rbacService;
1270
+ private readonly hookRegistry;
1258
1271
  private readonly host;
1259
1272
  private readonly injector;
1260
1273
  readonly currentData: i0.WritableSignal<Record<string, any>>;
@@ -1355,7 +1368,18 @@ declare class DynamicRecordFormComponent implements OnChanges {
1355
1368
  * validity of the tab's own controls, and rules filtered to this tab — a rule targeting
1356
1369
  * a hidden tab must not block the save (the reference's OV0-968 fix).
1357
1370
  */
1358
- saveSection(): void;
1371
+ saveSection(): Promise<void>;
1372
+ /**
1373
+ * Put a record to the entity's `beforeSave` hook.
1374
+ *
1375
+ * `sectionSave` carries the *whole* record — `extractRecord()`, not the tab's slice — so
1376
+ * the same payload reached persistence through two buttons, one of which asked the hook
1377
+ * and one of which did not. A hook registered to veto a save is a data-integrity
1378
+ * mechanism; having a second route around it made it advisory.
1379
+ *
1380
+ * @returns the record to emit, or `null` when the hook aborted (already reported).
1381
+ */
1382
+ private runBeforeSave;
1359
1383
  /**
1360
1384
  * Seed the session baseline from the loaded record so `VALUE_CHANGED` rules and the
1361
1385
  * modification banner compare against the record as it was opened — not as it was
@@ -1423,7 +1447,7 @@ declare class DynamicRecordFormComponent implements OnChanges {
1423
1447
  */
1424
1448
  jumpToField(fieldId: string): void;
1425
1449
  static ɵfac: i0.ɵɵFactoryDeclaration<DynamicRecordFormComponent, never>;
1426
- static ɵcmp: i0.ɵɵComponentDeclaration<DynamicRecordFormComponent, "ngx-dynamic-record-form", never, { "config": { "alias": "config"; "required": false; }; "rules": { "alias": "rules"; "required": false; }; "initialData": { "alias": "initialData"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "error": { "alias": "error"; "required": false; }; "isReadOnly": { "alias": "isReadOnly"; "required": false; }; "readOnlyFields": { "alias": "readOnlyFields"; "required": false; }; "viewMode": { "alias": "viewMode"; "required": false; }; }, { "formSubmit": "formSubmit"; "formChange": "formChange"; "formReset": "formReset"; "sectionSave": "sectionSave"; }, never, never, true, never>;
1450
+ static ɵcmp: i0.ɵɵComponentDeclaration<DynamicRecordFormComponent, "ngx-dynamic-record-form", never, { "config": { "alias": "config"; "required": false; }; "rules": { "alias": "rules"; "required": false; }; "initialData": { "alias": "initialData"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "error": { "alias": "error"; "required": false; }; "isReadOnly": { "alias": "isReadOnly"; "required": false; }; "readOnlyFields": { "alias": "readOnlyFields"; "required": false; }; "viewMode": { "alias": "viewMode"; "required": false; }; }, { "formSubmit": "formSubmit"; "formChange": "formChange"; "formReset": "formReset"; "sectionSave": "sectionSave"; "saveRejected": "saveRejected"; }, never, never, true, never>;
1427
1451
  }
1428
1452
 
1429
1453
  /**
@@ -1884,7 +1908,18 @@ declare class DateFieldComponent {
1884
1908
  readonly: boolean;
1885
1909
  masked: boolean;
1886
1910
  get label(): string;
1887
- /** ISO 8601 date → locale display string (ONEHERMES convention: always UTC, display in user locale) */
1911
+ /**
1912
+ * ISO 8601 date → display string, through the formatter the host configured.
1913
+ *
1914
+ * This used to call `toLocaleDateString()` directly, which meant `setDateFormatters` — the
1915
+ * one seam for choosing how a date is rendered — reached the record summary and the `time`
1916
+ * field but not this one. A host that configured formatters got them almost everywhere,
1917
+ * and silently did not get them on the field type most likely to be the reason they
1918
+ * configured formatters at all.
1919
+ *
1920
+ * The default formatter is `toLocaleDateString()` with no locale, so an application that
1921
+ * has not called `setDateFormatters` renders exactly what it rendered before.
1922
+ */
1888
1923
  formatDate(value: string | null): string;
1889
1924
  /**
1890
1925
  * Resolved through `ValidationMessagesService`, so `provideNgxDynamicEntity({
@@ -1925,9 +1960,17 @@ declare class DateTimeFieldComponent {
1925
1960
  /** The input's local `YYYY-MM-DDTHH:mm` → stored ISO 8601 UTC. */
1926
1961
  onInput(value: string): void;
1927
1962
  /**
1928
- * Displays date *and* time. `DateFieldComponent` used `toLocaleDateString()` here, so a
1929
- * datetime read back through the field showed no time while the same value in the record
1930
- * summary — which goes through core's `formatDisplayValue` — showed it.
1963
+ * Displays date *and* time, through the formatter the host configured.
1964
+ *
1965
+ * Two rounds of the same fault. First this called `toLocaleDateString()`, so a datetime
1966
+ * read back through the field showed no time while the record summary showed it. Then it
1967
+ * called `toLocaleString()` directly, which fixed the time but kept the field outside
1968
+ * `setDateFormatters` — so a host that configured formatters still saw them apply to the
1969
+ * summary and not to the field. Going through `formatDisplayValue` is what makes the two
1970
+ * paths agree by construction rather than by matching literals.
1971
+ *
1972
+ * `parseStored` accepts shapes `new Date(raw)` alone does not, so the *parsed* instant is
1973
+ * handed on as ISO rather than the raw value.
1931
1974
  */
1932
1975
  formatDateTime(value: unknown): string;
1933
1976
  /**