@nocobase/plugin-flow-engine 2.1.0-alpha.9 → 2.1.0-beta.10
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/dist/ai/docs/runjs/context/block-model.md +35 -35
- package/dist/ai/docs/runjs/context/collection-field.md +51 -53
- package/dist/ai/docs/runjs/context/collection.md +39 -39
- package/dist/ai/docs/runjs/context/data-source-manager.md +30 -40
- package/dist/ai/docs/runjs/context/data-source.md +44 -52
- package/dist/ai/docs/runjs/context/element.md +38 -44
- package/dist/ai/docs/runjs/context/exit-all.md +35 -37
- package/dist/ai/docs/runjs/context/exit.md +35 -38
- package/dist/ai/docs/runjs/context/filter-manager.md +30 -36
- package/dist/ai/docs/runjs/context/form.md +57 -57
- package/dist/ai/docs/runjs/context/get-model.md +21 -22
- package/dist/ai/docs/runjs/context/get-value.md +19 -20
- package/dist/ai/docs/runjs/context/get-var.md +55 -61
- package/dist/ai/docs/runjs/context/i18n.md +14 -17
- package/dist/ai/docs/runjs/context/import-async.md +45 -333
- package/dist/ai/docs/runjs/context/init-resource.md +20 -20
- package/dist/ai/docs/runjs/context/libs.md +31 -31
- package/dist/ai/docs/runjs/context/location.md +31 -34
- package/dist/ai/docs/runjs/context/logger.md +40 -41
- package/dist/ai/docs/runjs/context/make-resource.md +26 -27
- package/dist/ai/docs/runjs/context/message.md +41 -42
- package/dist/ai/docs/runjs/context/modal.md +44 -44
- package/dist/ai/docs/runjs/context/model.md +33 -36
- package/dist/ai/docs/runjs/context/notification.md +40 -41
- package/dist/ai/docs/runjs/context/off.md +14 -14
- package/dist/ai/docs/runjs/context/on.md +29 -30
- package/dist/ai/docs/runjs/context/open-view.md +40 -40
- package/dist/ai/docs/runjs/context/render.md +32 -37
- package/dist/ai/docs/runjs/context/request.md +45 -46
- package/dist/ai/docs/runjs/context/require-async.md +25 -28
- package/dist/ai/docs/runjs/context/resource.md +34 -34
- package/dist/ai/docs/runjs/context/route.md +34 -36
- package/dist/ai/docs/runjs/context/router.md +31 -43
- package/dist/ai/docs/runjs/context/set-value.md +17 -18
- package/dist/ai/docs/runjs/context/t.md +17 -20
- package/dist/ai/docs/runjs/context/view.md +46 -49
- package/dist/ai/docs/runjs/document.md +0 -1
- package/dist/ai/docs/runjs/import-modules.md +32 -32
- package/dist/ai/docs/runjs/index.md +13 -13
- package/dist/ai/docs/runjs/jsx.md +19 -19
- package/dist/ai/docs/runjs/model/form-block-model.md +3 -1
- package/dist/ai/docs/runjs/render.md +15 -15
- package/dist/ai/docs/runjs/resource/api-resource.md +53 -53
- package/dist/ai/docs/runjs/resource/multi-record-resource.md +64 -64
- package/dist/ai/docs/runjs/resource/single-record-resource.md +55 -55
- package/dist/ai/docs/runjs/resource/sql-resource.md +57 -57
- package/dist/ai/docs/runjs/window.md +5 -5
- package/dist/externalVersion.js +10 -10
- package/dist/node_modules/ses/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# ctx.dataSource
|
|
2
2
|
|
|
3
|
-
The `DataSource`
|
|
3
|
+
The data source instance (`DataSource`) bound to the current RunJS context; used to access collections, field metadata, and collection config **within that data source**. Usually the current page/block’s data source (e.g. main `main`).
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
7
|
| Scenario | Description |
|
|
8
|
-
|
|
9
|
-
| **Single
|
|
10
|
-
| **Collection
|
|
11
|
-
| **
|
|
8
|
+
|----------|-------------|
|
|
9
|
+
| **Single data source** | Get collections, field metadata when the current data source is known |
|
|
10
|
+
| **Collection management** | Get/add/update/remove collections in the current data source |
|
|
11
|
+
| **Field by path** | Get field definition by `collectionName.fieldPath` (supports association path) |
|
|
12
12
|
|
|
13
|
-
> Note: `ctx.dataSource`
|
|
13
|
+
> Note: `ctx.dataSource` is the single data source for the current context; to enumerate or access other data sources use [ctx.dataSourceManager](./data-source-manager.md).
|
|
14
14
|
|
|
15
|
-
## Type
|
|
15
|
+
## Type
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
dataSource: DataSource;
|
|
@@ -20,18 +20,15 @@ dataSource: DataSource;
|
|
|
20
20
|
class DataSource {
|
|
21
21
|
constructor(options?: Record<string, any>);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
get
|
|
25
|
-
get
|
|
26
|
-
get
|
|
27
|
-
get name(): string; // Same as key
|
|
23
|
+
get flowEngine(): FlowEngine;
|
|
24
|
+
get displayName(): string;
|
|
25
|
+
get key(): string;
|
|
26
|
+
get name(): string;
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
getAssociation(associationName: string): CollectionField | undefined; // Get association field (e.g., users.roles)
|
|
28
|
+
getCollections(): Collection[];
|
|
29
|
+
getCollection(name: string): Collection | undefined;
|
|
30
|
+
getAssociation(associationName: string): CollectionField | undefined;
|
|
33
31
|
|
|
34
|
-
// Collection management
|
|
35
32
|
addCollection(collection: Collection | CollectionOptions): void;
|
|
36
33
|
updateCollection(newOptions: CollectionOptions): void;
|
|
37
34
|
upsertCollection(options: CollectionOptions): Collection | undefined;
|
|
@@ -39,7 +36,6 @@ class DataSource {
|
|
|
39
36
|
removeCollection(name: string): void;
|
|
40
37
|
clearCollections(): void;
|
|
41
38
|
|
|
42
|
-
// Field metadata
|
|
43
39
|
getCollectionField(fieldPath: string): CollectionField | undefined;
|
|
44
40
|
}
|
|
45
41
|
```
|
|
@@ -47,61 +43,57 @@ class DataSource {
|
|
|
47
43
|
## Common Properties
|
|
48
44
|
|
|
49
45
|
| Property | Type | Description |
|
|
50
|
-
|
|
51
|
-
| `key` | `string` | Data source key
|
|
46
|
+
|----------|------|-------------|
|
|
47
|
+
| `key` | `string` | Data source key (e.g. `main`) |
|
|
52
48
|
| `name` | `string` | Same as key |
|
|
53
|
-
| `displayName` | `string` | Display name (
|
|
49
|
+
| `displayName` | `string` | Display name (i18n) |
|
|
54
50
|
| `flowEngine` | `FlowEngine` | Current FlowEngine instance |
|
|
55
51
|
|
|
56
52
|
## Common Methods
|
|
57
53
|
|
|
58
54
|
| Method | Description |
|
|
59
|
-
|
|
60
|
-
| `getCollections()` |
|
|
61
|
-
| `getCollection(name)` |
|
|
62
|
-
| `getAssociation(associationName)` |
|
|
63
|
-
| `getCollectionField(fieldPath)` |
|
|
55
|
+
|--------|-------------|
|
|
56
|
+
| `getCollections()` | All collections in this data source (sorted, hidden filtered) |
|
|
57
|
+
| `getCollection(name)` | Collection by name; `name` can be `collectionName.fieldName` for association target |
|
|
58
|
+
| `getAssociation(associationName)` | Association field by `collectionName.fieldName` |
|
|
59
|
+
| `getCollectionField(fieldPath)` | Field by `collectionName.fieldPath`; supports paths like `users.profile.avatar` |
|
|
64
60
|
|
|
65
|
-
##
|
|
61
|
+
## Relation to ctx.dataSourceManager
|
|
66
62
|
|
|
67
|
-
|
|
|
68
|
-
|
|
69
|
-
| **Single data source
|
|
70
|
-
| **Entry
|
|
71
|
-
| **
|
|
72
|
-
| **
|
|
73
|
-
| **
|
|
74
|
-
| **
|
|
63
|
+
| Need | Recommended |
|
|
64
|
+
|------|-------------|
|
|
65
|
+
| **Single data source for context** | `ctx.dataSource` |
|
|
66
|
+
| **Entry to all data sources** | `ctx.dataSourceManager` |
|
|
67
|
+
| **Collection in current data source** | `ctx.dataSource.getCollection(name)` |
|
|
68
|
+
| **Collection in another data source** | `ctx.dataSourceManager.getCollection(dataSourceKey, collectionName)` |
|
|
69
|
+
| **Field in current data source** | `ctx.dataSource.getCollectionField('users.profile.avatar')` |
|
|
70
|
+
| **Field across data sources** | `ctx.dataSourceManager.getCollectionField('main.users.profile.avatar')` |
|
|
75
71
|
|
|
76
|
-
##
|
|
72
|
+
## Examples
|
|
77
73
|
|
|
78
|
-
### Get
|
|
74
|
+
### Get collections and fields
|
|
79
75
|
|
|
80
76
|
```ts
|
|
81
|
-
// Get all collections
|
|
82
77
|
const collections = ctx.dataSource.getCollections();
|
|
83
78
|
|
|
84
|
-
// Get collection by name
|
|
85
79
|
const users = ctx.dataSource.getCollection('users');
|
|
86
80
|
const primaryKey = users?.filterTargetKey ?? 'id';
|
|
87
81
|
|
|
88
|
-
// Get field definition by "collectionName.fieldPath" (supports associations)
|
|
89
82
|
const field = ctx.dataSource.getCollectionField('users.profile.avatar');
|
|
90
83
|
const userNameField = ctx.dataSource.getCollectionField('orders.createdBy.name');
|
|
91
84
|
```
|
|
92
85
|
|
|
93
|
-
### Get
|
|
86
|
+
### Get association field
|
|
94
87
|
|
|
95
88
|
```ts
|
|
96
|
-
// Get association field definition by collectionName.fieldName
|
|
97
89
|
const rolesField = ctx.dataSource.getAssociation('users.roles');
|
|
98
90
|
if (rolesField?.isAssociationField()) {
|
|
99
91
|
const targetCol = rolesField.targetCollection;
|
|
100
|
-
//
|
|
92
|
+
// ...
|
|
101
93
|
}
|
|
102
94
|
```
|
|
103
95
|
|
|
104
|
-
### Iterate
|
|
96
|
+
### Iterate collections
|
|
105
97
|
|
|
106
98
|
```ts
|
|
107
99
|
const collections = ctx.dataSource.getCollections();
|
|
@@ -112,25 +104,25 @@ for (const col of collections) {
|
|
|
112
104
|
}
|
|
113
105
|
```
|
|
114
106
|
|
|
115
|
-
###
|
|
107
|
+
### Validation or dynamic UI from field metadata
|
|
116
108
|
|
|
117
109
|
```ts
|
|
118
110
|
const field = ctx.dataSource.getCollectionField('users.status');
|
|
119
111
|
if (field) {
|
|
120
112
|
const options = field.enum ?? [];
|
|
121
113
|
const operators = field.getFilterOperators();
|
|
122
|
-
//
|
|
114
|
+
// ...
|
|
123
115
|
}
|
|
124
116
|
```
|
|
125
117
|
|
|
126
118
|
## Notes
|
|
127
119
|
|
|
128
|
-
-
|
|
129
|
-
- `getCollection(name)` supports
|
|
130
|
-
- In
|
|
120
|
+
- `getCollectionField(fieldPath)` uses path format `collectionName.fieldPath`; first segment is collection name, rest is field path (supports association, e.g. `user.name`).
|
|
121
|
+
- `getCollection(name)` supports `collectionName.fieldName` and returns the association target collection.
|
|
122
|
+
- In RunJS, `ctx.dataSource` is usually determined by the current block/page; if there is no bound data source it may be `undefined`—check before use.
|
|
131
123
|
|
|
132
124
|
## Related
|
|
133
125
|
|
|
134
|
-
- [ctx.dataSourceManager](./data-source-manager.md):
|
|
135
|
-
- [ctx.collection](./collection.md):
|
|
136
|
-
- [ctx.collectionField](./collection-field.md):
|
|
126
|
+
- [ctx.dataSourceManager](./data-source-manager.md): manager for all data sources
|
|
127
|
+
- [ctx.collection](./collection.md): collection for current context
|
|
128
|
+
- [ctx.collectionField](./collection-field.md): current field’s collection field definition
|
|
@@ -1,60 +1,57 @@
|
|
|
1
1
|
# ctx.element
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The ElementProxy instance for the sandbox DOM container; it is the default render target of `ctx.render()`. Available in JSBlock, JSField, JSItem, JSColumn, and other contexts that have a render container.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Use Cases
|
|
6
6
|
|
|
7
7
|
| Scenario | Description |
|
|
8
|
-
|
|
9
|
-
| **JSBlock** |
|
|
10
|
-
| **JSField / JSItem / FormJSFieldItem** |
|
|
11
|
-
| **JSColumn** |
|
|
8
|
+
|----------|-------------|
|
|
9
|
+
| **JSBlock** | Block’s DOM container for custom content |
|
|
10
|
+
| **JSField / JSItem / FormJSFieldItem** | Field/item render container (often a `<span>`) |
|
|
11
|
+
| **JSColumn** | Table cell DOM container for custom column content |
|
|
12
12
|
|
|
13
|
-
> Note: `ctx.element` is only available in RunJS contexts that have a
|
|
13
|
+
> Note: `ctx.element` is only available in RunJS contexts that have a render container; in contexts without UI (e.g. pure backend) it may be `undefined`—check before use.
|
|
14
14
|
|
|
15
|
-
## Type
|
|
15
|
+
## Type
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
18
|
element: ElementProxy | undefined;
|
|
19
19
|
|
|
20
|
-
// ElementProxy is a proxy for the raw HTMLElement, exposing a secure API
|
|
21
20
|
class ElementProxy {
|
|
22
|
-
__el: HTMLElement; //
|
|
23
|
-
innerHTML: string; //
|
|
24
|
-
outerHTML: string;
|
|
21
|
+
__el: HTMLElement; // Internal native DOM (only for specific cases)
|
|
22
|
+
innerHTML: string; // Read/write sanitized with DOMPurify
|
|
23
|
+
outerHTML: string;
|
|
25
24
|
appendChild(child: HTMLElement | string): void;
|
|
26
|
-
// Other HTMLElement methods
|
|
25
|
+
// Other HTMLElement methods passed through (not recommended)
|
|
27
26
|
}
|
|
28
27
|
```
|
|
29
28
|
|
|
30
|
-
## Security
|
|
29
|
+
## Security
|
|
31
30
|
|
|
32
|
-
**Recommended:
|
|
31
|
+
**Recommended: do all rendering via `ctx.render()`.** Do not use `ctx.element`’s DOM APIs directly (e.g. `innerHTML`, `appendChild`, `querySelector`).
|
|
33
32
|
|
|
34
|
-
### Why ctx.render()
|
|
33
|
+
### Why use ctx.render()
|
|
35
34
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
| **Security** | Centralized
|
|
39
|
-
| **React
|
|
40
|
-
| **Context
|
|
41
|
-
| **
|
|
35
|
+
| Benefit | Description |
|
|
36
|
+
|---------|-------------|
|
|
37
|
+
| **Security** | Centralized control, avoids XSS and unsafe DOM use |
|
|
38
|
+
| **React** | Full JSX, components, and lifecycle |
|
|
39
|
+
| **Context** | Inherits app ConfigProvider, theme, etc. |
|
|
40
|
+
| **Conflicts** | Manages React root create/unmount, avoids multiple instances |
|
|
42
41
|
|
|
43
|
-
### ❌ Not
|
|
42
|
+
### ❌ Not recommended: direct ctx.element use
|
|
44
43
|
|
|
45
44
|
```ts
|
|
46
|
-
// ❌ Not recommended: Using ctx.element APIs directly
|
|
47
45
|
ctx.element.innerHTML = '<div>Content</div>';
|
|
48
46
|
ctx.element.appendChild(node);
|
|
49
47
|
ctx.element.querySelector('.class');
|
|
50
48
|
```
|
|
51
49
|
|
|
52
|
-
> `ctx.element.innerHTML` is deprecated
|
|
50
|
+
> `ctx.element.innerHTML` is deprecated; use `ctx.render()` instead.
|
|
53
51
|
|
|
54
|
-
### ✅ Recommended:
|
|
52
|
+
### ✅ Recommended: ctx.render()
|
|
55
53
|
|
|
56
54
|
```ts
|
|
57
|
-
// ✅ Rendering a React component
|
|
58
55
|
const { Button, Card } = ctx.libs.antd;
|
|
59
56
|
ctx.render(
|
|
60
57
|
<Card title={ctx.t('Welcome')}>
|
|
@@ -62,43 +59,40 @@ ctx.render(
|
|
|
62
59
|
</Card>
|
|
63
60
|
);
|
|
64
61
|
|
|
65
|
-
// ✅ Rendering an HTML string
|
|
66
62
|
ctx.render('<div style="padding:16px;">' + ctx.t('Content') + '</div>');
|
|
67
63
|
|
|
68
|
-
// ✅ Rendering a DOM node
|
|
69
64
|
const div = document.createElement('div');
|
|
70
65
|
div.textContent = ctx.t('Hello');
|
|
71
66
|
ctx.render(div);
|
|
72
67
|
```
|
|
73
68
|
|
|
74
|
-
##
|
|
69
|
+
## Exception: popover anchor
|
|
75
70
|
|
|
76
|
-
When you need
|
|
71
|
+
When you need the current element as a popover anchor, use `ctx.element?.__el` as the native DOM `target`:
|
|
77
72
|
|
|
78
73
|
```ts
|
|
79
|
-
// ctx.viewer.popover requires a raw DOM as the target
|
|
80
74
|
await ctx.viewer.popover({
|
|
81
75
|
target: ctx.element?.__el,
|
|
82
|
-
content: <div>
|
|
76
|
+
content: <div>Popover content</div>,
|
|
83
77
|
});
|
|
84
78
|
```
|
|
85
79
|
|
|
86
|
-
> Use `__el` only
|
|
80
|
+
> Use `__el` only for this “current container as anchor” case; do not touch DOM otherwise.
|
|
87
81
|
|
|
88
|
-
##
|
|
82
|
+
## Relation to ctx.render
|
|
89
83
|
|
|
90
|
-
-
|
|
91
|
-
- If
|
|
92
|
-
- You can
|
|
84
|
+
- `ctx.render(vnode)` without a `container` argument renders into `ctx.element`.
|
|
85
|
+
- If there is no `ctx.element` and no `container`, an error is thrown.
|
|
86
|
+
- You can pass a container: `ctx.render(vnode, customContainer)`.
|
|
93
87
|
|
|
94
88
|
## Notes
|
|
95
89
|
|
|
96
|
-
- `ctx.element`
|
|
97
|
-
- In contexts without a
|
|
98
|
-
-
|
|
90
|
+
- Treat `ctx.element` as the internal container for `ctx.render()`; avoid reading or mutating it directly.
|
|
91
|
+
- In contexts without a render container, `ctx.element` is `undefined`; ensure a container exists or pass `container` to `ctx.render()`.
|
|
92
|
+
- ElementProxy’s `innerHTML`/`outerHTML` are sanitized with DOMPurify, but prefer `ctx.render()` for all rendering.
|
|
99
93
|
|
|
100
94
|
## Related
|
|
101
95
|
|
|
102
|
-
- [ctx.render](./render.md):
|
|
103
|
-
- [ctx.view](./view.md):
|
|
104
|
-
- [ctx.modal](./modal.md):
|
|
96
|
+
- [ctx.render](./render.md): render into container
|
|
97
|
+
- [ctx.view](./view.md): current view controller
|
|
98
|
+
- [ctx.modal](./modal.md): modal APIs
|
|
@@ -1,96 +1,94 @@
|
|
|
1
1
|
# ctx.exitAll()
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stops the current event flow and all **subsequent** event flows that were triggered in the same event dispatch. Use when a global error or permission check requires stopping every flow for that event.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
|
-
`ctx.exitAll()`
|
|
7
|
+
Use `ctx.exitAll()` in JS-capable contexts when you need to **stop both the current flow and any later flows for the same event**:
|
|
8
8
|
|
|
9
9
|
| Scenario | Description |
|
|
10
|
-
|
|
11
|
-
| **Event
|
|
12
|
-
| **Linkage
|
|
13
|
-
| **Action
|
|
10
|
+
|----------|-------------|
|
|
11
|
+
| **Event flow** | Main flow fails (e.g. no permission); stop it and any subsequent flows for that event |
|
|
12
|
+
| **Linkage rules** | When linkage validation fails and you want to stop current and subsequent linkage |
|
|
13
|
+
| **Action events** | Pre-action check fails (e.g. delete permission); block the action and later steps |
|
|
14
14
|
|
|
15
|
-
> Difference from `ctx.exit()`: `ctx.exit()` only
|
|
15
|
+
> Difference from `ctx.exit()`: `ctx.exit()` only stops the current flow; `ctx.exitAll()` also stops **subsequent** flows for that event.
|
|
16
16
|
|
|
17
|
-
## Type
|
|
17
|
+
## Type
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
exitAll(): never;
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Calling `ctx.exitAll()` throws an internal `FlowExitAllException`, which
|
|
23
|
+
Calling `ctx.exitAll()` throws an internal `FlowExitAllException`, which the engine uses to stop the current flow instance and subsequent flows for the same event. The rest of the current JS does not run.
|
|
24
24
|
|
|
25
25
|
## Comparison with ctx.exit()
|
|
26
26
|
|
|
27
27
|
| Method | Scope |
|
|
28
|
-
|
|
29
|
-
| `ctx.exit()` |
|
|
30
|
-
| `ctx.exitAll()` |
|
|
28
|
+
|--------|--------|
|
|
29
|
+
| `ctx.exit()` | Stops only the current flow; later flows still run |
|
|
30
|
+
| `ctx.exitAll()` | Stops the current flow and **subsequent** flows for the same event |
|
|
31
31
|
|
|
32
|
-
## Execution
|
|
32
|
+
## Execution mode
|
|
33
33
|
|
|
34
|
-
- **Sequential
|
|
35
|
-
- **Parallel
|
|
34
|
+
- **Sequential**: flows for the same event run one after another; after any flow calls `ctx.exitAll()`, later flows do not run.
|
|
35
|
+
- **Parallel**: flows run in parallel; one flow calling `ctx.exitAll()` does not stop other already-running flows.
|
|
36
36
|
|
|
37
37
|
## Examples
|
|
38
38
|
|
|
39
|
-
###
|
|
39
|
+
### Stop all flows on permission failure
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
// Abort the main event flow and subsequent event flows when permissions are insufficient
|
|
43
42
|
if (!hasPermission(ctx)) {
|
|
44
|
-
ctx.notification.error({ message: 'No
|
|
43
|
+
ctx.notification.error({ message: 'No permission' });
|
|
45
44
|
ctx.exitAll();
|
|
46
45
|
}
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
###
|
|
48
|
+
### Stop on global pre-check failure
|
|
50
49
|
|
|
51
50
|
```ts
|
|
52
|
-
// Example: If associated data is found to be non-deletable before deletion, prevent the main event flow and subsequent actions
|
|
53
51
|
const canDelete = await checkDeletable(ctx.model?.getValue?.());
|
|
54
52
|
if (!canDelete) {
|
|
55
|
-
ctx.message.error('Cannot delete:
|
|
53
|
+
ctx.message.error('Cannot delete: related data exists');
|
|
56
54
|
ctx.exitAll();
|
|
57
55
|
}
|
|
58
56
|
```
|
|
59
57
|
|
|
60
|
-
###
|
|
58
|
+
### When to use ctx.exit() vs ctx.exitAll()
|
|
61
59
|
|
|
62
60
|
```ts
|
|
63
|
-
// Only
|
|
61
|
+
// Only this flow → ctx.exit()
|
|
64
62
|
if (!params.valid) {
|
|
65
|
-
ctx.message.error('Invalid
|
|
66
|
-
ctx.exit();
|
|
63
|
+
ctx.message.error('Invalid params');
|
|
64
|
+
ctx.exit();
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
//
|
|
67
|
+
// This and all subsequent flows → ctx.exitAll()
|
|
70
68
|
if (!ctx.model?.context?.getPermission?.()) {
|
|
71
|
-
ctx.notification.warning({ message: '
|
|
72
|
-
ctx.exitAll();
|
|
69
|
+
ctx.notification.warning({ message: 'No permission' });
|
|
70
|
+
ctx.exitAll();
|
|
73
71
|
}
|
|
74
72
|
```
|
|
75
73
|
|
|
76
|
-
###
|
|
74
|
+
### Message then exit
|
|
77
75
|
|
|
78
76
|
```ts
|
|
79
77
|
if (!isValidInput(ctx.form?.getValues?.())) {
|
|
80
|
-
ctx.message.warning('Please
|
|
78
|
+
ctx.message.warning('Please fix form errors first');
|
|
81
79
|
ctx.exitAll();
|
|
82
80
|
}
|
|
83
81
|
```
|
|
84
82
|
|
|
85
83
|
## Notes
|
|
86
84
|
|
|
87
|
-
- After
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
- In parallel mode, `ctx.exitAll()` only
|
|
85
|
+
- After `ctx.exitAll()`, the rest of the current JS does not run; explain to the user with `ctx.message`, `ctx.notification`, or a dialog before calling.
|
|
86
|
+
- You usually do not need to catch `FlowExitAllException`; the engine handles it.
|
|
87
|
+
- To stop only the current flow, use `ctx.exit()`.
|
|
88
|
+
- In parallel mode, `ctx.exitAll()` only stops the current flow; it does not cancel other concurrent flows.
|
|
91
89
|
|
|
92
90
|
## Related
|
|
93
91
|
|
|
94
|
-
- [ctx.exit()](./exit.md):
|
|
95
|
-
- [ctx.message](./message.md):
|
|
96
|
-
- [ctx.modal](./modal.md):
|
|
92
|
+
- [ctx.exit()](./exit.md): stop only the current flow
|
|
93
|
+
- [ctx.message](./message.md): message API
|
|
94
|
+
- [ctx.modal](./modal.md): confirm dialog
|
|
@@ -1,89 +1,86 @@
|
|
|
1
1
|
# ctx.exit()
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stops the current event flow; later steps in that flow do not run. Often used when business conditions fail, the user cancels, or an unrecoverable error occurs.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
|
-
`ctx.exit()` is
|
|
7
|
+
`ctx.exit()` is used in contexts that execute JS, such as:
|
|
8
8
|
|
|
9
9
|
| Scenario | Description |
|
|
10
|
-
|
|
11
|
-
| **Event
|
|
12
|
-
| **Linkage
|
|
13
|
-
| **Action
|
|
10
|
+
|----------|-------------|
|
|
11
|
+
| **Event flow** | In flows triggered by form submit, button click, etc., stop when conditions are not met |
|
|
12
|
+
| **Linkage rules** | Field or filter linkage; stop when validation fails or execution should be skipped |
|
|
13
|
+
| **Action events** | In custom actions (e.g. delete confirm, pre-save validation), exit when user cancels or validation fails |
|
|
14
14
|
|
|
15
|
-
> Difference from `ctx.exitAll()`: `ctx.exit()` only
|
|
15
|
+
> Difference from `ctx.exitAll()`: `ctx.exit()` only stops the **current** event flow; other flows for the same event still run. `ctx.exitAll()` also stops **subsequent** flows for that event.
|
|
16
16
|
|
|
17
|
-
## Type
|
|
17
|
+
## Type
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
exit(): never;
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Calling `ctx.exit()` throws an internal `FlowExitException`, which
|
|
23
|
+
Calling `ctx.exit()` throws an internal `FlowExitException`, which the event flow engine catches and uses to stop the current flow. Once called, the rest of the current JS does not run.
|
|
24
24
|
|
|
25
25
|
## Comparison with ctx.exitAll()
|
|
26
26
|
|
|
27
|
-
| Method | Scope
|
|
28
|
-
|
|
29
|
-
| `ctx.exit()` |
|
|
30
|
-
| `ctx.exitAll()` |
|
|
27
|
+
| Method | Scope |
|
|
28
|
+
|--------|--------|
|
|
29
|
+
| `ctx.exit()` | Stops only the current event flow; others unaffected |
|
|
30
|
+
| `ctx.exitAll()` | Stops the current flow and **subsequent** flows for the same event |
|
|
31
31
|
|
|
32
32
|
## Examples
|
|
33
33
|
|
|
34
|
-
### Exit on
|
|
34
|
+
### Exit on user cancel
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
|
-
// In a confirmation modal, terminate the event flow if the user clicks cancel
|
|
38
37
|
if (!confirmed) {
|
|
39
|
-
ctx.message.info('
|
|
38
|
+
ctx.message.info('Cancelled');
|
|
40
39
|
ctx.exit();
|
|
41
40
|
}
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
### Exit on
|
|
43
|
+
### Exit on validation failure
|
|
45
44
|
|
|
46
45
|
```ts
|
|
47
|
-
// Prompt and terminate when validation fails
|
|
48
46
|
if (!params.value || params.value.length < 3) {
|
|
49
|
-
ctx.message.error('Invalid
|
|
47
|
+
ctx.message.error('Invalid: length must be at least 3');
|
|
50
48
|
ctx.exit();
|
|
51
49
|
}
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
### Exit
|
|
52
|
+
### Exit when business condition fails
|
|
55
53
|
|
|
56
54
|
```ts
|
|
57
|
-
// Terminate if conditions are not met; subsequent steps will not execute
|
|
58
55
|
const record = ctx.model?.getValue?.();
|
|
59
56
|
if (!record || record.status !== 'draft') {
|
|
60
|
-
ctx.notification.warning({ message: 'Only
|
|
57
|
+
ctx.notification.warning({ message: 'Only draft can be submitted' });
|
|
61
58
|
ctx.exit();
|
|
62
59
|
}
|
|
63
60
|
```
|
|
64
61
|
|
|
65
|
-
###
|
|
62
|
+
### When to use ctx.exit() vs ctx.exitAll()
|
|
66
63
|
|
|
67
64
|
```ts
|
|
68
|
-
// Only
|
|
65
|
+
// Only this flow should stop → ctx.exit()
|
|
69
66
|
if (!params.valid) {
|
|
70
|
-
ctx.message.error('Invalid
|
|
71
|
-
ctx.exit();
|
|
67
|
+
ctx.message.error('Invalid params');
|
|
68
|
+
ctx.exit();
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
//
|
|
71
|
+
// Stop this and all subsequent flows for this event → ctx.exitAll()
|
|
75
72
|
if (!ctx.model?.context?.getPermission?.()) {
|
|
76
|
-
ctx.notification.warning({ message: '
|
|
77
|
-
ctx.exitAll();
|
|
73
|
+
ctx.notification.warning({ message: 'No permission' });
|
|
74
|
+
ctx.exitAll();
|
|
78
75
|
}
|
|
79
76
|
```
|
|
80
77
|
|
|
81
|
-
### Exit
|
|
78
|
+
### Exit after modal confirm
|
|
82
79
|
|
|
83
80
|
```ts
|
|
84
81
|
const ok = await ctx.modal?.confirm?.({
|
|
85
|
-
title: 'Confirm
|
|
86
|
-
content: '
|
|
82
|
+
title: 'Confirm delete',
|
|
83
|
+
content: 'Cannot be recovered. Continue?',
|
|
87
84
|
});
|
|
88
85
|
if (!ok) {
|
|
89
86
|
ctx.message.info('Cancelled');
|
|
@@ -93,12 +90,12 @@ if (!ok) {
|
|
|
93
90
|
|
|
94
91
|
## Notes
|
|
95
92
|
|
|
96
|
-
- After
|
|
97
|
-
-
|
|
98
|
-
-
|
|
93
|
+
- After `ctx.exit()`, the rest of the current JS does not run; use `ctx.message`, `ctx.notification`, or a dialog before calling to explain why.
|
|
94
|
+
- You usually do not need to catch `FlowExitException`; the event flow engine handles it.
|
|
95
|
+
- To stop all subsequent flows for the same event, use `ctx.exitAll()`.
|
|
99
96
|
|
|
100
97
|
## Related
|
|
101
98
|
|
|
102
|
-
- [ctx.exitAll()](./exit-all.md):
|
|
103
|
-
- [ctx.message](./message.md):
|
|
104
|
-
- [ctx.modal](./modal.md):
|
|
99
|
+
- [ctx.exitAll()](./exit-all.md): stop current and subsequent flows for the event
|
|
100
|
+
- [ctx.message](./message.md): message API
|
|
101
|
+
- [ctx.modal](./modal.md): confirm dialog
|