@smartbit4all/ng-client 7.0.6 → 7.0.9

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/README.md CHANGED
@@ -1,199 +1,199 @@
1
- # @smartbit4all/ng-client
2
-
3
- The Angular client of the smartbit4all platform: a backend-driven UI layer where the server
4
- owns the view lifecycle and the client renders it. Screen components extend
5
- `SmartComponent`, talk to the platform's BFF (`ViewApi` / `PageApi`), and the widgets inside
6
- them — grid, tree, form, filter, map, diagram, toolbars — reload themselves when the backend
7
- says their part of the model changed.
8
-
9
- **Requires Angular 22 and Material 22.** Everything is standalone; there are no NgModules.
10
-
11
- - Upgrading from 6.x? **`MIGRATION-7.0.md`**, shipped next to this file, is the whole change
12
- list, and there is a codemod for the mechanical part.
13
- - Writing your own widget? **`WIDGETS.md`**, also next to this file.
14
-
15
- ---
16
-
17
- ## Installation
18
-
19
- ```bash
20
- npm i @smartbit4all/ng-client
21
- npm i @angular/material-date-fns-adapter@^22.0.0 date-fns@^4
22
- ```
23
-
24
- ## Wiring it up
25
-
26
- One call provides the whole library. It is deliberately not optional per module: the session
27
- and header interceptors used to be easy to leave out, and leaving them out failed *silently*
28
- — with no `Authorization` header.
29
-
30
- ```ts
31
- bootstrapApplication(AppComponent, {
32
- providers: [
33
- provideAnimations(),
34
- provideRouter(ROUTES),
35
-
36
- provideSmartNgClient(
37
- {
38
- gridMenuIcon: 'more_horiz',
39
- aclEditingViewName: Pages.ACL_MATRIX_PAGE,
40
- namedValidators: [MY_VALIDATOR_FACTORY],
41
- },
42
- withSmartMap({ engine: MapEngine.LEAFLET }), // optional: pulls in a map engine
43
- withSmartDiagram({ customOptions: [MY_CHART] }) // optional: pulls in chart.js
44
- ),
45
-
46
- { provide: HTTP_INTERCEPTORS, useClass: MyLoadingInterceptor, multi: true },
47
- ],
48
- });
49
- ```
50
-
51
- `SmartNgClientConfig` is the entire configuration surface — see its doc comments for the
52
- fields. Only the map and the diagram are opt-in, because they are the only features that pull
53
- heavy third-party code.
54
-
55
- Components are standalone: import the ones your templates use in the component that uses
56
- them.
57
-
58
- ```ts
59
- @Component({
60
- selector: 'app-my-page',
61
- templateUrl: './my-page.component.html',
62
- imports: [SmartGridComponent, UiActionToolbarComponent, SmartEmbeddedSlotDirective],
63
- })
64
- export class MyPageComponent extends SmartComponent<MyModel> { … }
65
- ```
66
-
67
- ## Session and view context
68
-
69
- `SmartBackendBootstrapService` owns the startup sequence. Configure it once, then choose one
70
- entry point — `start()` for a normal web app, `bootstrapWith(creds)` when a native shell
71
- hands the session over. They are mutually exclusive; `reset()` switches.
72
-
73
- ```ts
74
- @Injectable({ providedIn: 'root' })
75
- export class AppAuthBootstrap {
76
- constructor(
77
- private bootstrap: SmartBackendBootstrapService,
78
- defaultErrorUi: SmartDefaultErrorUiService
79
- ) {
80
- this.bootstrap.configure({
81
- url: 'https://my-host/api',
82
- cookieName: 'my-app',
83
- viewHandlers: HANDLERS,
84
- actionDescriptors: ACTION_DESCRIPTORS,
85
- ...defaultErrorUi.hooks({ language: 'hu' }),
86
- });
87
- this.bootstrap.start();
88
- }
89
- }
90
- ```
91
-
92
- ```ts
93
- providers: [provideAppInitializer(() => { inject(AppAuthBootstrap); })]
94
- ```
95
-
96
- Every screen component `await`s `bootstrap.whenReady()` before its first BFF call —
97
- `SmartComponentApiClient.run()` does it for you. The config hooks (`onStartError`,
98
- `onSessionError`, `onViewContextLost`, `onSmartLink`) are where host-specific behaviour goes;
99
- the error interceptor routes backend session errors into them.
100
-
101
- ### View handlers
102
-
103
- The backend names the view; the host says what renders it.
104
-
105
- ```ts
106
- export const HANDLERS: SmartViewHandlerModel[] = [
107
- { name: Pages.ANY_PAGE, route: 'any' }, // ViewType.NORMAL
108
- { name: Pages.ANY_DIALOG, component: AnyDialogComponent }, // ViewType.DIALOG
109
- { name: Pages.ANY_COMPONENT, route: 'any', component: AnyComponent },
110
- ];
111
- ```
112
-
113
- A view the host did not register still renders, through the library's default components;
114
- add your own with `defaultViewComponents`.
115
-
116
- ### Smartlinks
117
-
118
- Route `/redirect/:channel/:uuid` to `SmartViewRedirect` (or to your own component extending
119
- it) and handle the hand-off in `configure({ onSmartLink })`.
120
-
121
- ```ts
122
- const routes: Routes = [
123
- {
124
- path: `redirect/:${SmartLinkChannelVariableInPath}/:${SmartLinkUuidVariableInPath}`,
125
- component: SmartViewRedirect,
126
- },
127
- ];
128
- ```
129
-
130
- ## UiActions
131
-
132
- The backend sends the actions; the host supplies their *descriptors* — caption, icon, button
133
- type, confirm/input dialog, feedback:
134
-
135
- ```ts
136
- this.viewContext.setActionDescriptors(ACTION_DESCRIPTORS);
137
- this.viewContext.commonFeedbackText = 'Done.';
138
- ```
139
-
140
- A toolbar renders the actions **addressed to its `id`** (`uiAction.toolbar == id`), pulling
141
- them from the screen component above it; an explicit `[uiActionModels]` binding wins. Without
142
- an `id` and without a binding it shows nothing.
143
-
144
- ```html
145
- <smart-ui-action-toolbar [id]="'myToolbar'"></smart-ui-action-toolbar>
146
- <smart-ui-action-toolbar [uiActionModels]="myActions" [executor]="myService"></smart-ui-action-toolbar>
147
- ```
148
-
149
- `[executor]` defaults to the screen component the toolbar sits under. Bind it only when the
150
- actions belong to another API — a tree service, a dialog service of your own — and implement
151
- `UiActionExecutor` there:
152
-
153
- ```ts
154
- export class MyDialogService implements UiActionExecutor {
155
- submitForm(validate: boolean): void { … }
156
- getInvalidFields(): SmartFormInvalidFields { … }
157
- getAdditionalParams(uiAction: UiAction): UiActionAdditionalParams { … }
158
- getModel(): any { … }
159
- performUiActionRequest(request: UiActionRequest): Promise<any> { … }
160
- handleSpecificDemandsAsynchronously(…): Promise<UiActionSpecificDemandResponse> { … }
161
- }
162
- ```
163
-
164
- A `UiActionModel` is **frozen**: build an entry, never edit one. To change how an action
165
- looks, rebuild the array — `actions.map(a => a.uiAction.code === c ? { ...a, cssClass: 'x' } : a)`
166
- — because it is the array reference changing that re-renders the toolbar.
167
-
168
- ## Change detection
169
-
170
- 7.0 runs without zone.js and every library component is `OnPush`. Nothing forces your host to
171
- drop zone.js — the library works either way — but if you do, the same rule applies to your
172
- own components: state written from a subscription, a promise or a timer needs a signal or a
173
- `markForCheck()`; state written from a template event or an input does not.
174
-
175
- ## Dates
176
-
177
- The browser's timezone on screen, Zulu (`…Z`) on the wire, the server converts. Date widgets
178
- hold plain `Date` values and render `yyyy.MM.dd.`; the adapter is date-fns. If you provide
179
- `MAT_DATE_LOCALE`, the string `'hu-HU'` keeps working — see `MIGRATION-7.0.md`, *Dates*.
180
-
181
- ## Dev tool
182
-
183
- ```ts
184
- localStorage.setItem('smartDevToolActive', 'true'); // active
185
- localStorage.setItem('useDevTool', 'true'); // button visible
186
- ```
187
-
188
- ---
189
-
190
- ## Further reading
191
-
192
- | File | What it is |
193
- |---|---|
194
- | `MIGRATION-7.0.md` | the 6.x → 7.0 change list, and the codemod that does the mechanical part |
195
- | `WIDGETS.md` | the widget-authoring protocol |
196
- | `MIGRATION-6.0.md`, `MIGRATION-4.5.md` | the previous two majors |
197
-
198
- Per-module notes and version logs live next to the sources, in
199
- `src/lib/<module>/README.md` and `src/lib/<module>/versionLogs.md`.
1
+ # @smartbit4all/ng-client
2
+
3
+ The Angular client of the smartbit4all platform: a backend-driven UI layer where the server
4
+ owns the view lifecycle and the client renders it. Screen components extend
5
+ `SmartComponent`, talk to the platform's BFF (`ViewApi` / `PageApi`), and the widgets inside
6
+ them — grid, tree, form, filter, map, diagram, toolbars — reload themselves when the backend
7
+ says their part of the model changed.
8
+
9
+ **Requires Angular 22 and Material 22.** Everything is standalone; there are no NgModules.
10
+
11
+ - Upgrading from 6.x? **`MIGRATION-7.0.md`**, shipped next to this file, is the whole change
12
+ list, and there is a codemod for the mechanical part.
13
+ - Writing your own widget? **`WIDGETS.md`**, also next to this file.
14
+
15
+ ---
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm i @smartbit4all/ng-client
21
+ npm i @angular/material-date-fns-adapter@^22.0.0 date-fns@^4
22
+ ```
23
+
24
+ ## Wiring it up
25
+
26
+ One call provides the whole library. It is deliberately not optional per module: the session
27
+ and header interceptors used to be easy to leave out, and leaving them out failed *silently*
28
+ — with no `Authorization` header.
29
+
30
+ ```ts
31
+ bootstrapApplication(AppComponent, {
32
+ providers: [
33
+ provideAnimations(),
34
+ provideRouter(ROUTES),
35
+
36
+ provideSmartNgClient(
37
+ {
38
+ gridMenuIcon: 'more_horiz',
39
+ aclEditingViewName: Pages.ACL_MATRIX_PAGE,
40
+ namedValidators: [MY_VALIDATOR_FACTORY],
41
+ },
42
+ withSmartMap({ engine: MapEngine.LEAFLET }), // optional: pulls in a map engine
43
+ withSmartDiagram({ customOptions: [MY_CHART] }) // optional: pulls in chart.js
44
+ ),
45
+
46
+ { provide: HTTP_INTERCEPTORS, useClass: MyLoadingInterceptor, multi: true },
47
+ ],
48
+ });
49
+ ```
50
+
51
+ `SmartNgClientConfig` is the entire configuration surface — see its doc comments for the
52
+ fields. Only the map and the diagram are opt-in, because they are the only features that pull
53
+ heavy third-party code.
54
+
55
+ Components are standalone: import the ones your templates use in the component that uses
56
+ them.
57
+
58
+ ```ts
59
+ @Component({
60
+ selector: 'app-my-page',
61
+ templateUrl: './my-page.component.html',
62
+ imports: [SmartGridComponent, UiActionToolbarComponent, SmartEmbeddedSlotDirective],
63
+ })
64
+ export class MyPageComponent extends SmartComponent<MyModel> { … }
65
+ ```
66
+
67
+ ## Session and view context
68
+
69
+ `SmartBackendBootstrapService` owns the startup sequence. Configure it once, then choose one
70
+ entry point — `start()` for a normal web app, `bootstrapWith(creds)` when a native shell
71
+ hands the session over. They are mutually exclusive; `reset()` switches.
72
+
73
+ ```ts
74
+ @Injectable({ providedIn: 'root' })
75
+ export class AppAuthBootstrap {
76
+ constructor(
77
+ private bootstrap: SmartBackendBootstrapService,
78
+ defaultErrorUi: SmartDefaultErrorUiService
79
+ ) {
80
+ this.bootstrap.configure({
81
+ url: 'https://my-host/api',
82
+ cookieName: 'my-app',
83
+ viewHandlers: HANDLERS,
84
+ actionDescriptors: ACTION_DESCRIPTORS,
85
+ ...defaultErrorUi.hooks({ language: 'hu' }),
86
+ });
87
+ this.bootstrap.start();
88
+ }
89
+ }
90
+ ```
91
+
92
+ ```ts
93
+ providers: [provideAppInitializer(() => { inject(AppAuthBootstrap); })]
94
+ ```
95
+
96
+ Every screen component `await`s `bootstrap.whenReady()` before its first BFF call —
97
+ `SmartComponentApiClient.run()` does it for you. The config hooks (`onStartError`,
98
+ `onSessionError`, `onViewContextLost`, `onSmartLink`) are where host-specific behaviour goes;
99
+ the error interceptor routes backend session errors into them.
100
+
101
+ ### View handlers
102
+
103
+ The backend names the view; the host says what renders it.
104
+
105
+ ```ts
106
+ export const HANDLERS: SmartViewHandlerModel[] = [
107
+ { name: Pages.ANY_PAGE, route: 'any' }, // ViewType.NORMAL
108
+ { name: Pages.ANY_DIALOG, component: AnyDialogComponent }, // ViewType.DIALOG
109
+ { name: Pages.ANY_COMPONENT, route: 'any', component: AnyComponent },
110
+ ];
111
+ ```
112
+
113
+ A view the host did not register still renders, through the library's default components;
114
+ add your own with `defaultViewComponents`.
115
+
116
+ ### Smartlinks
117
+
118
+ Route `/redirect/:channel/:uuid` to `SmartViewRedirect` (or to your own component extending
119
+ it) and handle the hand-off in `configure({ onSmartLink })`.
120
+
121
+ ```ts
122
+ const routes: Routes = [
123
+ {
124
+ path: `redirect/:${SmartLinkChannelVariableInPath}/:${SmartLinkUuidVariableInPath}`,
125
+ component: SmartViewRedirect,
126
+ },
127
+ ];
128
+ ```
129
+
130
+ ## UiActions
131
+
132
+ The backend sends the actions; the host supplies their *descriptors* — caption, icon, button
133
+ type, confirm/input dialog, feedback:
134
+
135
+ ```ts
136
+ this.viewContext.setActionDescriptors(ACTION_DESCRIPTORS);
137
+ this.viewContext.commonFeedbackText = 'Done.';
138
+ ```
139
+
140
+ A toolbar renders the actions **addressed to its `id`** (`uiAction.toolbar == id`), pulling
141
+ them from the screen component above it; an explicit `[uiActionModels]` binding wins. Without
142
+ an `id` and without a binding it shows nothing.
143
+
144
+ ```html
145
+ <smart-ui-action-toolbar [id]="'myToolbar'"></smart-ui-action-toolbar>
146
+ <smart-ui-action-toolbar [uiActionModels]="myActions" [executor]="myService"></smart-ui-action-toolbar>
147
+ ```
148
+
149
+ `[executor]` defaults to the screen component the toolbar sits under. Bind it only when the
150
+ actions belong to another API — a tree service, a dialog service of your own — and implement
151
+ `UiActionExecutor` there:
152
+
153
+ ```ts
154
+ export class MyDialogService implements UiActionExecutor {
155
+ submitForm(validate: boolean): void { … }
156
+ getInvalidFields(): SmartFormInvalidFields { … }
157
+ getAdditionalParams(uiAction: UiAction): UiActionAdditionalParams { … }
158
+ getModel(): any { … }
159
+ performUiActionRequest(request: UiActionRequest): Promise<any> { … }
160
+ handleSpecificDemandsAsynchronously(…): Promise<UiActionSpecificDemandResponse> { … }
161
+ }
162
+ ```
163
+
164
+ A `UiActionModel` is **frozen**: build an entry, never edit one. To change how an action
165
+ looks, rebuild the array — `actions.map(a => a.uiAction.code === c ? { ...a, cssClass: 'x' } : a)`
166
+ — because it is the array reference changing that re-renders the toolbar.
167
+
168
+ ## Change detection
169
+
170
+ 7.0 runs without zone.js and every library component is `OnPush`. Nothing forces your host to
171
+ drop zone.js — the library works either way — but if you do, the same rule applies to your
172
+ own components: state written from a subscription, a promise or a timer needs a signal or a
173
+ `markForCheck()`; state written from a template event or an input does not.
174
+
175
+ ## Dates
176
+
177
+ The browser's timezone on screen, Zulu (`…Z`) on the wire, the server converts. Date widgets
178
+ hold plain `Date` values and render `yyyy.MM.dd.`; the adapter is date-fns. If you provide
179
+ `MAT_DATE_LOCALE`, the string `'hu-HU'` keeps working — see `MIGRATION-7.0.md`, *Dates*.
180
+
181
+ ## Dev tool
182
+
183
+ ```ts
184
+ localStorage.setItem('smartDevToolActive', 'true'); // active
185
+ localStorage.setItem('useDevTool', 'true'); // button visible
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Further reading
191
+
192
+ | File | What it is |
193
+ |---|---|
194
+ | `MIGRATION-7.0.md` | the 6.x → 7.0 change list, and the codemod that does the mechanical part |
195
+ | `WIDGETS.md` | the widget-authoring protocol |
196
+ | `MIGRATION-6.0.md`, `MIGRATION-4.5.md` | the previous two majors |
197
+
198
+ Per-module notes and version logs live next to the sources, in
199
+ `src/lib/<module>/README.md` and `src/lib/<module>/versionLogs.md`.