@smartbit4all/ng-client 7.0.10 → 7.1.1
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/MIGRATION-7.0.md +128 -1
- package/fesm2022/smartbit4all-ng-client.mjs +522 -176
- package/fesm2022/smartbit4all-ng-client.mjs.map +1 -1
- package/package.json +1 -1
- package/smartbit4all-ng-client-7.1.1.tgz +0 -0
- package/types/smartbit4all-ng-client.d.ts +128 -18
- package/smartbit4all-ng-client-7.0.10.tgz +0 -0
package/MIGRATION-7.0.md
CHANGED
|
@@ -1252,7 +1252,7 @@ shorts link produced no player. It plays now.
|
|
|
1252
1252
|
|
|
1253
1253
|
| Removed | Replacement |
|
|
1254
1254
|
|---|---|
|
|
1255
|
-
| `SmartformwidgetComponent` | nothing — no host imported the class; `<smartform>` still renders it. 7.
|
|
1255
|
+
| `SmartformwidgetComponent` | nothing — no host imported the class; `<smartform>` still renders it. 7.2 splits it into per-type components |
|
|
1256
1256
|
| `SmartWidgetSettings` (its only member was `static useUtc`) | nothing — **delete the assignment** |
|
|
1257
1257
|
|
|
1258
1258
|
3.6 moved the `useUtc` static into `SmartNgClientConfig.useUtcDates`; **3.8 then removed the
|
|
@@ -1717,6 +1717,133 @@ keep its own default when the library passed `false` or `''` — it now receives
|
|
|
1717
1717
|
This is the only part of 7.0 where a bugfix can change what you see on screen without any
|
|
1718
1718
|
code of yours changing.
|
|
1719
1719
|
|
|
1720
|
+
### Model sync: the client's models travel with every call (7.1.0)
|
|
1721
|
+
|
|
1722
|
+
Whether an action carried the page model used to be a per-action decision of the backend
|
|
1723
|
+
(`UiAction.submit` / `model` / `composite`) plus one hidden rule (an action with no params sent
|
|
1724
|
+
it anyway), and the model travelled under three ad-hoc `params` keys (`model`, `clientPageModel`,
|
|
1725
|
+
`compositeModel`) that only one endpoint wrote into the backend view automatically. The bug that
|
|
1726
|
+
forced the change: an action on an embedded view sent only its own model, the handler changed the
|
|
1727
|
+
container's model, the container's whole component model came back, and whatever the user had
|
|
1728
|
+
typed into the container since the last round-trip was gone.
|
|
1729
|
+
|
|
1730
|
+
From 7.1.0 the client sends, with **every** action, widget action, upload and grid selection
|
|
1731
|
+
call, the data of **every open view that changed since it was last synced** — slot-hosted and
|
|
1732
|
+
dialog views alike — in a new `UiActionRequest.modelSync.models` map keyed by view uuid. The
|
|
1733
|
+
backend writes each one into the matching view **before the handler runs and before it takes the
|
|
1734
|
+
base of its change push**, so a handler always sees what is on screen and the client's own values
|
|
1735
|
+
are never echoed back. A view equal to its last synced copy is left out; a view without a copy is
|
|
1736
|
+
always sent.
|
|
1737
|
+
|
|
1738
|
+
**Backend minimum, and the server switch.** The platform build that ships
|
|
1739
|
+
`UiActionRequest.modelSync` (platform ADR-0011 on the server side), **with model sync switched
|
|
1740
|
+
on**: `view.model-sync.enabled=true` in the host's `application.properties`. The switch defaults
|
|
1741
|
+
to `false`, and in that mode the server behaves exactly as before model sync existed: nothing is
|
|
1742
|
+
written into the views before the handler, and a request that carries `modelSync` is **rejected**
|
|
1743
|
+
with an error naming the property, not ignored. A 7.1.0 client sends the envelope with every
|
|
1744
|
+
call, so against a server with the switch off every call fails loudly; against an older platform
|
|
1745
|
+
the field is silently ignored and every handler that read `params.model` sees nothing. **Order:
|
|
1746
|
+
upgrade the backend, set the flag, then upgrade the client.** Server code can read the mode as
|
|
1747
|
+
`viewApi.isModelSyncEnabled()`.
|
|
1748
|
+
|
|
1749
|
+
**A 6.x client against a backend with the switch on** is rejected too: with
|
|
1750
|
+
`view.model-sync.enabled=true` every request that can carry `modelSync` — an action, a widget or
|
|
1751
|
+
upload action, a tree action, a grid selection change — must carry it, and one without it (an
|
|
1752
|
+
old client, or host code that builds a `UiActionRequest` by hand and calls the view context
|
|
1753
|
+
service directly) gets an error saying so. A call that cannot carry it (loading a view, a
|
|
1754
|
+
message result, expanding a tree node, a map interaction) is neither synced nor refused. The old `params.model` / `clientPageModel` / `compositeModel`
|
|
1755
|
+
keys are never read as a sync source; they are only *filled* by the backend, see below. So the
|
|
1756
|
+
switch and the client upgrade go together per host: flip the flag in the same deploy that ships
|
|
1757
|
+
the 7.1.0 client. Internal Java callers with nothing to sync send an empty `new ModelSync()`.
|
|
1758
|
+
|
|
1759
|
+
#### What changed on the client
|
|
1760
|
+
|
|
1761
|
+
- `UiAction.model` and `UiAction.composite` are **deprecated no-ops** for a view's own actions.
|
|
1762
|
+
`submit` keeps one meaning: validate before sending, the embedded views' forms included. For an
|
|
1763
|
+
executor that is not a view (a tree, the filter editor) `model` still flushes its form and its
|
|
1764
|
+
model still rides on `params.model` as before, because it has no view to sync.
|
|
1765
|
+
- Forms are flushed into their data (`submitForm(false)`) on every open view before a call, so an
|
|
1766
|
+
unsaved field value is part of what is compared and sent. **Only the user's edits are
|
|
1767
|
+
flushed**: the form remembers, per control, the value it last exchanged with the data (built
|
|
1768
|
+
or refreshed from it, or written into it), and a control still equal to that writes nothing.
|
|
1769
|
+
This is what keeps an untouched `false` or `0` from turning into `''` on a round trip (the
|
|
1770
|
+
control is now built with `value ?? ''`, and a value written back takes the type the field has
|
|
1771
|
+
in the data: `'true'` → `true`, `'12'` → `12`, `''` → `null`), and a backend push from
|
|
1772
|
+
reaching the data only to be overwritten by the control's stale value on the next call. The
|
|
1773
|
+
same rule the other way: a data field the backend changed reaches its control even when the
|
|
1774
|
+
user edited it (the backend wins on a field it changed), a field it did not change keeps what
|
|
1775
|
+
the user typed. `SmartFormService.resetExchangedValues()` starts over when a form is rebuilt.
|
|
1776
|
+
The form's own dirty flag is not involved: it lives until the form is rebuilt and says nothing
|
|
1777
|
+
about what the data holds.
|
|
1778
|
+
- A push of some `data.*` paths marks only those paths as synced; whatever else the view holds
|
|
1779
|
+
keeps its standing, so a value written into the data outside a form while a call was in flight
|
|
1780
|
+
is still sent by the next one. A whole-model push and the initial load mark all of it.
|
|
1781
|
+
- Calls run concurrently, and an envelope built earlier may complete later: its copies are never
|
|
1782
|
+
written over the copies a later envelope or push already settled. The server-side ordering of
|
|
1783
|
+
rapid per-field actions stays out of scope (ADR-0010).
|
|
1784
|
+
- A dialog the backend closed (`ViewState.TO_CLOSE`) no longer fires a `DEFAULT_CLOSE` action of
|
|
1785
|
+
its own on top of the response that closed it; only the user's own close does.
|
|
1786
|
+
- The per-field actions a widget's `valueChangeMode` fires are ordinary actions and inherit the
|
|
1787
|
+
sync; they no longer set `model: true`, and still carry `params.item`.
|
|
1788
|
+
|
|
1789
|
+
#### Removed from the public API
|
|
1790
|
+
|
|
1791
|
+
| Gone | What to do |
|
|
1792
|
+
|---|---|
|
|
1793
|
+
| `SmartComponentApiClient.sendModelOnWidgetAction` | Delete the assignment. Every widget action carries the changed models now. Loud: `this.sendModelOnWidgetAction = true` no longer compiles. |
|
|
1794
|
+
| `SmartComponentApiClient.addModelToWidgetParams()` / `addModelToWidgetAction()` | Delete. The grid service builds its own envelope. |
|
|
1795
|
+
| `SmartViewContextService.getCompositeModel()` | Delete. The backend gets every changed view. |
|
|
1796
|
+
| `SmartViewContextService.submitEmbeddedForms()` | `validateEmbeddedForms(containerUuid)` — validation only; the flush is part of every call. |
|
|
1797
|
+
| `SmartViewContextService.dataChanged()` | Delete. It was never wired on the client (`handleValueChanges` had no caller); a field change is a uiAction. |
|
|
1798
|
+
|
|
1799
|
+
New on `SmartViewContextService`: `joinModelSync(uuid, participant)` / `leaveModelSync(uuid,
|
|
1800
|
+
participant)` (the client does this on its own uuid; leaving only takes effect for the participant
|
|
1801
|
+
that is registered, so a component destroyed after its successor joined under the same uuid does
|
|
1802
|
+
not take the successor with it), `markModelSynced(uuid, keys?)` (the client calls it whenever the
|
|
1803
|
+
backend's model reached it: without keys the whole data, with keys the pushed `data` paths), and `withModelSync(carrier, call)` for a host that issues its own backend call on a request
|
|
1804
|
+
that can carry the envelope (a `UiActionRequest`, a `GridSelectionChange`): it puts the envelope
|
|
1805
|
+
on the carrier, runs the call with it and settles the copies on success, and hands back the raw
|
|
1806
|
+
`ViewContextChange` — what to do with the response stays with the caller. The grid and tree
|
|
1807
|
+
services are the in-tree examples; `beginModelSync()` / `completeModelSync()` underneath are
|
|
1808
|
+
public too.
|
|
1809
|
+
|
|
1810
|
+
#### The Java side of the host: three kinds of handler
|
|
1811
|
+
|
|
1812
|
+
The backend **backfills** the old `params` keys from the synced model on every request (with the
|
|
1813
|
+
switch on, every request carries `modelSync`), so no handler has to change on the day the client
|
|
1814
|
+
is upgraded. Two kinds must be
|
|
1815
|
+
looked at anyway, and one of them breaks without a compiler error. Run
|
|
1816
|
+
`node tools/model-sync-audit/cli.mjs ../<host>` — it lists every read of the request model with
|
|
1817
|
+
one of these categories and a reason ([its README](../../tools/model-sync-audit/README.md)):
|
|
1818
|
+
|
|
1819
|
+
| | The handler | What to do |
|
|
1820
|
+
|---|---|---|
|
|
1821
|
+
| **A** | reads `params.model` (`extractClientModel(request)`, `actionRequestHelper(request).get(UiActions.MODEL, …)`) and only passes it to `setModel(viewUuid, …)` | Delete both statements. The sync already wrote the client's model into the view. |
|
|
1822
|
+
| **B** | reads it for something else: saves it, validates it, forwards it, mutates it before setting it back | Nothing required. `getModel(viewUuid)` is the honest read now; `extractClientModel` is `@Deprecated`. A grid row action's `params.model` is still the clicked row and is never synced. |
|
|
1823
|
+
| **C** | reads the view's model too (`getModel(viewUuid)`, `view.getModel()`) and uses the two together — a diff, a merge, "what changed" | **Breaks silently.** After the sync `getModel` *is* the client's model, so the comparison compares a thing with itself. The server-side read must become `viewApi.isModelSyncEnabled() ? getPreviousModel(viewUuid) : getModel(viewUuid)`: the model as the server knew it before this call, request-scoped (`viewApi.getPreviousModel(uuid, Class)` outside a `PageApiImpl`). The off branch is the old code. |
|
|
1824
|
+
|
|
1825
|
+
A handler that must work in both server modes — a shared module deployed to hosts with the
|
|
1826
|
+
switch off as well as on — branches inline on `viewApi.isModelSyncEnabled()` and keeps its old
|
|
1827
|
+
code verbatim in the off branch: an A site becomes
|
|
1828
|
+
`if (!viewApi.isModelSyncEnabled()) { setModel(viewUuid, extractClientModel(request)); }`, a
|
|
1829
|
+
client read `viewApi.isModelSyncEnabled() ? getModel(viewUuid) : extractClientModel(request)`, a
|
|
1830
|
+
C site's server read the ternary above. No wrapper helper on purpose: once every host runs with
|
|
1831
|
+
the switch on, the off branches are deleted line by line and what remains is the plain new code.
|
|
1832
|
+
The platform's own handlers are written this way. `getPreviousModel` with the switch off returns
|
|
1833
|
+
`getModel` and logs an ERROR: that code assumes a mode the server is not running in.
|
|
1834
|
+
|
|
1835
|
+
Two traps the audit cannot see:
|
|
1836
|
+
|
|
1837
|
+
- A handler that forwards the *request* to a callback which reads `params.model` (the platform's
|
|
1838
|
+
password editor does this) depends on `extractClientModel` writing the typed instance back into
|
|
1839
|
+
the params. Keep `extractClientModel` there, or hand the model over explicitly.
|
|
1840
|
+
- A handler that reads `compositeModel` by hand gets it backfilled only when its action still
|
|
1841
|
+
has `composite=true`. Read the children's models from `viewApi` instead; the flag goes away
|
|
1842
|
+
once no host reads the key.
|
|
1843
|
+
|
|
1844
|
+
The measured spread, over the 56 host repositories that were audited when this shipped: 1061 read
|
|
1845
|
+
sites, 301 A, 671 B, 77 C.
|
|
1846
|
+
|
|
1720
1847
|
### Bugfixes shipped with 7.0
|
|
1721
1848
|
|
|
1722
1849
|
- `SmartformwidgetComponent.ngAfterViewInit` no longer crashes with
|