@smartbit4all/ng-client 7.0.5 → 7.0.6
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 +1722 -1722
- package/README.md +199 -199
- package/WIDGETS.md +206 -206
- package/fesm2022/smartbit4all-ng-client.mjs +148 -144
- package/fesm2022/smartbit4all-ng-client.mjs.map +1 -1
- package/package.json +1 -1
- package/smartbit4all-ng-client-7.0.6.tgz +0 -0
- package/smartbit4all-ng-client-7.0.5.tgz +0 -0
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`.
|