@nocobase/plugin-flow-engine 2.1.0-alpha.6 → 2.1.0-alpha.8
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 +53 -51
- package/dist/ai/docs/runjs/context/collection.md +39 -39
- package/dist/ai/docs/runjs/context/data-source-manager.md +40 -30
- package/dist/ai/docs/runjs/context/data-source.md +52 -44
- package/dist/ai/docs/runjs/context/element.md +44 -38
- package/dist/ai/docs/runjs/context/exit-all.md +37 -35
- package/dist/ai/docs/runjs/context/exit.md +38 -35
- package/dist/ai/docs/runjs/context/filter-manager.md +36 -30
- package/dist/ai/docs/runjs/context/form.md +57 -57
- package/dist/ai/docs/runjs/context/get-model.md +22 -21
- package/dist/ai/docs/runjs/context/get-value.md +20 -19
- package/dist/ai/docs/runjs/context/get-var.md +61 -55
- package/dist/ai/docs/runjs/context/i18n.md +17 -14
- package/dist/ai/docs/runjs/context/import-async.md +333 -45
- 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 +34 -31
- package/dist/ai/docs/runjs/context/logger.md +41 -40
- package/dist/ai/docs/runjs/context/make-resource.md +27 -26
- package/dist/ai/docs/runjs/context/message.md +42 -41
- package/dist/ai/docs/runjs/context/modal.md +44 -44
- package/dist/ai/docs/runjs/context/model.md +36 -33
- package/dist/ai/docs/runjs/context/notification.md +41 -40
- package/dist/ai/docs/runjs/context/off.md +14 -14
- package/dist/ai/docs/runjs/context/on.md +30 -29
- package/dist/ai/docs/runjs/context/open-view.md +40 -40
- package/dist/ai/docs/runjs/context/render.md +37 -32
- package/dist/ai/docs/runjs/context/request.md +46 -45
- package/dist/ai/docs/runjs/context/require-async.md +28 -25
- package/dist/ai/docs/runjs/context/resource.md +34 -34
- package/dist/ai/docs/runjs/context/route.md +36 -34
- package/dist/ai/docs/runjs/context/router.md +43 -31
- package/dist/ai/docs/runjs/context/set-value.md +18 -17
- package/dist/ai/docs/runjs/context/sql.md +7 -15
- package/dist/ai/docs/runjs/context/t.md +20 -17
- package/dist/ai/docs/runjs/context/view.md +49 -46
- package/dist/ai/docs/runjs/document.md +1 -0
- 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 +1 -3
- 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/dist/server/collections/flowsql.js +1 -0
- package/package.json +2 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# ctx.dataSource
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The `DataSource` instance bound to the current RunJS execution context, used to access collections, field metadata, and manage collection configurations **within the current data source**. It usually corresponds to the data source selected for the current page or block (e.g., the main database `main`).
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
7
|
| Scenario | Description |
|
|
8
|
-
|
|
9
|
-
| **Single
|
|
10
|
-
| **Collection
|
|
11
|
-
| **
|
|
8
|
+
|------|------|
|
|
9
|
+
| **Single Data Source Operations** | Get collection and field metadata when the current data source is known. |
|
|
10
|
+
| **Collection Management** | Get, add, update, or delete collections under the current data source. |
|
|
11
|
+
| **Get Fields by Path** | Use the `collectionName.fieldPath` format to get field definitions (supports association paths). |
|
|
12
12
|
|
|
13
|
-
> Note: `ctx.dataSource`
|
|
13
|
+
> Note: `ctx.dataSource` represents a single data source for the current context. To enumerate or access other data sources, please use [ctx.dataSourceManager](./data-source-manager.md).
|
|
14
14
|
|
|
15
|
-
## Type
|
|
15
|
+
## Type Definition
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
dataSource: DataSource;
|
|
@@ -20,15 +20,18 @@ dataSource: DataSource;
|
|
|
20
20
|
class DataSource {
|
|
21
21
|
constructor(options?: Record<string, any>);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
get
|
|
25
|
-
get
|
|
26
|
-
get
|
|
23
|
+
// Read-only properties
|
|
24
|
+
get flowEngine(): FlowEngine; // Current FlowEngine instance
|
|
25
|
+
get displayName(): string; // Display name (supports i18n)
|
|
26
|
+
get key(): string; // Data source key, e.g., 'main'
|
|
27
|
+
get name(): string; // Same as key
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// Collection reading
|
|
30
|
+
getCollections(): Collection[]; // Get all collections
|
|
31
|
+
getCollection(name: string): Collection | undefined; // Get collection by name
|
|
32
|
+
getAssociation(associationName: string): CollectionField | undefined; // Get association field (e.g., users.roles)
|
|
31
33
|
|
|
34
|
+
// Collection management
|
|
32
35
|
addCollection(collection: Collection | CollectionOptions): void;
|
|
33
36
|
updateCollection(newOptions: CollectionOptions): void;
|
|
34
37
|
upsertCollection(options: CollectionOptions): Collection | undefined;
|
|
@@ -36,6 +39,7 @@ class DataSource {
|
|
|
36
39
|
removeCollection(name: string): void;
|
|
37
40
|
clearCollections(): void;
|
|
38
41
|
|
|
42
|
+
// Field metadata
|
|
39
43
|
getCollectionField(fieldPath: string): CollectionField | undefined;
|
|
40
44
|
}
|
|
41
45
|
```
|
|
@@ -43,57 +47,61 @@ class DataSource {
|
|
|
43
47
|
## Common Properties
|
|
44
48
|
|
|
45
49
|
| Property | Type | Description |
|
|
46
|
-
|
|
47
|
-
| `key` | `string` | Data source key
|
|
50
|
+
|------|------|------|
|
|
51
|
+
| `key` | `string` | Data source key, e.g., `'main'` |
|
|
48
52
|
| `name` | `string` | Same as key |
|
|
49
|
-
| `displayName` | `string` | Display name (i18n) |
|
|
53
|
+
| `displayName` | `string` | Display name (supports i18n) |
|
|
50
54
|
| `flowEngine` | `FlowEngine` | Current FlowEngine instance |
|
|
51
55
|
|
|
52
56
|
## Common Methods
|
|
53
57
|
|
|
54
58
|
| Method | Description |
|
|
55
|
-
|
|
56
|
-
| `getCollections()` |
|
|
57
|
-
| `getCollection(name)` |
|
|
58
|
-
| `getAssociation(associationName)` |
|
|
59
|
-
| `getCollectionField(fieldPath)` |
|
|
59
|
+
|------|------|
|
|
60
|
+
| `getCollections()` | Gets all collections under the current data source (sorted, with hidden ones filtered). |
|
|
61
|
+
| `getCollection(name)` | Gets a collection by name; `name` can be `collectionName.fieldName` to get the target collection of an association. |
|
|
62
|
+
| `getAssociation(associationName)` | Gets an association field definition by `collectionName.fieldName`. |
|
|
63
|
+
| `getCollectionField(fieldPath)` | Gets a field definition by `collectionName.fieldPath`, supporting association paths like `users.profile.avatar`. |
|
|
60
64
|
|
|
61
|
-
##
|
|
65
|
+
## Relationship with ctx.dataSourceManager
|
|
62
66
|
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
| **Single data source
|
|
66
|
-
| **Entry
|
|
67
|
-
| **
|
|
68
|
-
| **
|
|
69
|
-
| **
|
|
70
|
-
| **
|
|
67
|
+
| Requirement | Recommended Usage |
|
|
68
|
+
|------|----------|
|
|
69
|
+
| **Single data source bound to current context** | `ctx.dataSource` |
|
|
70
|
+
| **Entry point for all data sources** | `ctx.dataSourceManager` |
|
|
71
|
+
| **Get collection within current data source** | `ctx.dataSource.getCollection(name)` |
|
|
72
|
+
| **Get collection across data sources** | `ctx.dataSourceManager.getCollection(dataSourceKey, collectionName)` |
|
|
73
|
+
| **Get field within current data source** | `ctx.dataSource.getCollectionField('users.profile.avatar')` |
|
|
74
|
+
| **Get field across data sources** | `ctx.dataSourceManager.getCollectionField('main.users.profile.avatar')` |
|
|
71
75
|
|
|
72
|
-
##
|
|
76
|
+
## Example
|
|
73
77
|
|
|
74
|
-
### Get
|
|
78
|
+
### Get Collections and Fields
|
|
75
79
|
|
|
76
80
|
```ts
|
|
81
|
+
// Get all collections
|
|
77
82
|
const collections = ctx.dataSource.getCollections();
|
|
78
83
|
|
|
84
|
+
// Get collection by name
|
|
79
85
|
const users = ctx.dataSource.getCollection('users');
|
|
80
86
|
const primaryKey = users?.filterTargetKey ?? 'id';
|
|
81
87
|
|
|
88
|
+
// Get field definition by "collectionName.fieldPath" (supports associations)
|
|
82
89
|
const field = ctx.dataSource.getCollectionField('users.profile.avatar');
|
|
83
90
|
const userNameField = ctx.dataSource.getCollectionField('orders.createdBy.name');
|
|
84
91
|
```
|
|
85
92
|
|
|
86
|
-
### Get
|
|
93
|
+
### Get Association Fields
|
|
87
94
|
|
|
88
95
|
```ts
|
|
96
|
+
// Get association field definition by collectionName.fieldName
|
|
89
97
|
const rolesField = ctx.dataSource.getAssociation('users.roles');
|
|
90
98
|
if (rolesField?.isAssociationField()) {
|
|
91
99
|
const targetCol = rolesField.targetCollection;
|
|
92
|
-
//
|
|
100
|
+
// Process based on target collection structure
|
|
93
101
|
}
|
|
94
102
|
```
|
|
95
103
|
|
|
96
|
-
### Iterate
|
|
104
|
+
### Iterate Through Collections for Dynamic Processing
|
|
97
105
|
|
|
98
106
|
```ts
|
|
99
107
|
const collections = ctx.dataSource.getCollections();
|
|
@@ -104,25 +112,25 @@ for (const col of collections) {
|
|
|
104
112
|
}
|
|
105
113
|
```
|
|
106
114
|
|
|
107
|
-
### Validation or
|
|
115
|
+
### Perform Validation or Dynamic UI Based on Field Metadata
|
|
108
116
|
|
|
109
117
|
```ts
|
|
110
118
|
const field = ctx.dataSource.getCollectionField('users.status');
|
|
111
119
|
if (field) {
|
|
112
120
|
const options = field.enum ?? [];
|
|
113
121
|
const operators = field.getFilterOperators();
|
|
114
|
-
//
|
|
122
|
+
// Perform UI logic or validation based on interface, enum, validation, etc.
|
|
115
123
|
}
|
|
116
124
|
```
|
|
117
125
|
|
|
118
126
|
## Notes
|
|
119
127
|
|
|
120
|
-
- `getCollectionField(fieldPath)`
|
|
121
|
-
- `getCollection(name)` supports `collectionName.fieldName`
|
|
122
|
-
- In RunJS, `ctx.dataSource` is usually determined by the current block
|
|
128
|
+
- The path format for `getCollectionField(fieldPath)` is `collectionName.fieldPath`, where the first segment is the collection name and the subsequent segments are the field path (supports associations, e.g., `user.name`).
|
|
129
|
+
- `getCollection(name)` supports the `collectionName.fieldName` format, returning the target collection of the association field.
|
|
130
|
+
- In the RunJS context, `ctx.dataSource` is usually determined by the data source of the current block or page. If no data source is bound to the context, it may be `undefined`; it is recommended to perform a null check before use.
|
|
123
131
|
|
|
124
132
|
## Related
|
|
125
133
|
|
|
126
|
-
- [ctx.dataSourceManager](./data-source-manager.md): manager
|
|
127
|
-
- [ctx.collection](./collection.md): collection
|
|
128
|
-
- [ctx.collectionField](./collection-field.md):
|
|
134
|
+
- [ctx.dataSourceManager](./data-source-manager.md): Data source manager, manages all data sources.
|
|
135
|
+
- [ctx.collection](./collection.md): The collection associated with the current context.
|
|
136
|
+
- [ctx.collectionField](./collection-field.md): The collection field definition for the current field.
|
|
@@ -1,57 +1,60 @@
|
|
|
1
1
|
# ctx.element
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An `ElementProxy` instance pointing to the sandbox DOM container, serving as the default rendering target for `ctx.render()`. It is available in scenarios where a rendering container exists, such as `JSBlock`, `JSField`, `JSItem`, and `JSColumn`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Applicable Scenarios
|
|
6
6
|
|
|
7
7
|
| Scenario | Description |
|
|
8
|
-
|
|
9
|
-
| **JSBlock** |
|
|
10
|
-
| **JSField / JSItem / FormJSFieldItem** |
|
|
11
|
-
| **JSColumn** |
|
|
8
|
+
|------|------|
|
|
9
|
+
| **JSBlock** | The DOM container for the block, used to render custom block content. |
|
|
10
|
+
| **JSField / JSItem / FormJSFieldItem** | The rendering container for a field or form item (usually a `<span>`). |
|
|
11
|
+
| **JSColumn** | The DOM container for a table cell, used to render 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 rendering container. In contexts without a UI (such as pure backend logic), it may be `undefined`. It is recommended to perform a null check before use.
|
|
14
14
|
|
|
15
|
-
## Type
|
|
15
|
+
## Type Definition
|
|
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
|
|
20
21
|
class ElementProxy {
|
|
21
|
-
__el: HTMLElement; //
|
|
22
|
-
innerHTML: string; //
|
|
23
|
-
outerHTML: string;
|
|
22
|
+
__el: HTMLElement; // The internal raw DOM element (accessible only in specific scenarios)
|
|
23
|
+
innerHTML: string; // Sanitized via DOMPurify during read/write
|
|
24
|
+
outerHTML: string; // Same as above
|
|
24
25
|
appendChild(child: HTMLElement | string): void;
|
|
25
|
-
// Other HTMLElement methods passed through (not recommended)
|
|
26
|
+
// Other HTMLElement methods are passed through (direct use is not recommended)
|
|
26
27
|
}
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
## Security
|
|
30
|
+
## Security Requirements
|
|
30
31
|
|
|
31
|
-
**Recommended:
|
|
32
|
+
**Recommended: All rendering should be performed via `ctx.render()`.** Avoid using the DOM APIs of `ctx.element` directly (e.g., `innerHTML`, `appendChild`, `querySelector`, etc.).
|
|
32
33
|
|
|
33
|
-
### Why
|
|
34
|
+
### Why ctx.render() is Recommended
|
|
34
35
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
| **Security** | Centralized control
|
|
38
|
-
| **React** | Full JSX, components, and
|
|
39
|
-
| **Context** |
|
|
40
|
-
| **
|
|
36
|
+
| Advantage | Description |
|
|
37
|
+
|------|------|
|
|
38
|
+
| **Security** | Centralized security control to prevent XSS and improper DOM operations. |
|
|
39
|
+
| **React Support** | Full support for JSX, React components, and lifecycles. |
|
|
40
|
+
| **Context Inheritance** | Automatically inherits the application's `ConfigProvider`, themes, etc. |
|
|
41
|
+
| **Conflict Handling** | Automatically manages React root creation/unmounting to avoid multi-instance conflicts. |
|
|
41
42
|
|
|
42
|
-
### ❌ Not
|
|
43
|
+
### ❌ Not Recommended: Direct Manipulation of ctx.element
|
|
43
44
|
|
|
44
45
|
```ts
|
|
46
|
+
// ❌ Not recommended: Using ctx.element APIs directly
|
|
45
47
|
ctx.element.innerHTML = '<div>Content</div>';
|
|
46
48
|
ctx.element.appendChild(node);
|
|
47
49
|
ctx.element.querySelector('.class');
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
> `ctx.element.innerHTML` is deprecated
|
|
52
|
+
> `ctx.element.innerHTML` is deprecated. Please use `ctx.render()` instead.
|
|
51
53
|
|
|
52
|
-
### ✅ Recommended: ctx.render()
|
|
54
|
+
### ✅ Recommended: Using ctx.render()
|
|
53
55
|
|
|
54
56
|
```ts
|
|
57
|
+
// ✅ Rendering a React component
|
|
55
58
|
const { Button, Card } = ctx.libs.antd;
|
|
56
59
|
ctx.render(
|
|
57
60
|
<Card title={ctx.t('Welcome')}>
|
|
@@ -59,40 +62,43 @@ ctx.render(
|
|
|
59
62
|
</Card>
|
|
60
63
|
);
|
|
61
64
|
|
|
65
|
+
// ✅ Rendering an HTML string
|
|
62
66
|
ctx.render('<div style="padding:16px;">' + ctx.t('Content') + '</div>');
|
|
63
67
|
|
|
68
|
+
// ✅ Rendering a DOM node
|
|
64
69
|
const div = document.createElement('div');
|
|
65
70
|
div.textContent = ctx.t('Hello');
|
|
66
71
|
ctx.render(div);
|
|
67
72
|
```
|
|
68
73
|
|
|
69
|
-
##
|
|
74
|
+
## Special Case: As a Popover Anchor
|
|
70
75
|
|
|
71
|
-
When you need the current element as
|
|
76
|
+
When you need to open a Popover using the current element as an anchor, you can access `ctx.element?.__el` to get the raw DOM as the `target`:
|
|
72
77
|
|
|
73
78
|
```ts
|
|
79
|
+
// ctx.viewer.popover requires a raw DOM as the target
|
|
74
80
|
await ctx.viewer.popover({
|
|
75
81
|
target: ctx.element?.__el,
|
|
76
|
-
content: <div>
|
|
82
|
+
content: <div>Popup Content</div>,
|
|
77
83
|
});
|
|
78
84
|
```
|
|
79
85
|
|
|
80
|
-
> Use `__el` only
|
|
86
|
+
> Use `__el` only in scenarios like "using the current container as an anchor"; do not manipulate the DOM directly in other cases.
|
|
81
87
|
|
|
82
|
-
##
|
|
88
|
+
## Relationship with ctx.render
|
|
83
89
|
|
|
84
|
-
- `ctx.render(vnode)` without a `container` argument renders into `ctx.element
|
|
85
|
-
- If
|
|
86
|
-
- You can
|
|
90
|
+
- If `ctx.render(vnode)` is called without a `container` argument, it renders into the `ctx.element` container by default.
|
|
91
|
+
- If both `ctx.element` is missing and no `container` is provided, an error will be thrown.
|
|
92
|
+
- You can explicitly specify a container: `ctx.render(vnode, customContainer)`.
|
|
87
93
|
|
|
88
94
|
## Notes
|
|
89
95
|
|
|
90
|
-
-
|
|
91
|
-
- In contexts without a
|
|
92
|
-
-
|
|
96
|
+
- `ctx.element` is intended for internal use by `ctx.render()`. Directly accessing or modifying its properties/methods is not recommended.
|
|
97
|
+
- In contexts without a rendering container, `ctx.element` will be `undefined`. Ensure the container is available or pass a `container` manually before calling `ctx.render()`.
|
|
98
|
+
- Although `innerHTML`/`outerHTML` in `ElementProxy` are sanitized via DOMPurify, it is still recommended to use `ctx.render()` for unified rendering management.
|
|
93
99
|
|
|
94
100
|
## Related
|
|
95
101
|
|
|
96
|
-
- [ctx.render](./render.md):
|
|
97
|
-
- [ctx.view](./view.md):
|
|
98
|
-
- [ctx.modal](./modal.md):
|
|
102
|
+
- [ctx.render](./render.md): Rendering content into a container
|
|
103
|
+
- [ctx.view](./view.md): Current view controller
|
|
104
|
+
- [ctx.modal](./modal.md): Shortcut API for modals
|
|
@@ -1,94 +1,96 @@
|
|
|
1
1
|
# ctx.exitAll()
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Terminates the current event flow and all subsequent event flows triggered in the same event dispatch. It is commonly used when all event flows under the current event need to be aborted immediately due to a global error or permission validation failure.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`ctx.exitAll()` is generally used in JS-executable contexts where it is necessary to **simultaneously abort the current event flow and subsequent event flows triggered by that event**:
|
|
8
8
|
|
|
9
9
|
| Scenario | Description |
|
|
10
|
-
|
|
11
|
-
| **Event
|
|
12
|
-
| **Linkage
|
|
13
|
-
| **Action
|
|
10
|
+
|------|------|
|
|
11
|
+
| **Event Flow** | Main event flow validation fails (e.g., insufficient permissions), requiring the termination of the main flow and any subsequent flows under the same event that have not yet executed. |
|
|
12
|
+
| **Linkage Rules** | When linkage validation fails, the current linkage and subsequent linkages triggered by the same event must be terminated. |
|
|
13
|
+
| **Action Events** | Pre-action validation fails (e.g., permission check before deletion), requiring the prevention of the main action and subsequent steps. |
|
|
14
14
|
|
|
15
|
-
> Difference from `ctx.exit()`: `ctx.exit()` only
|
|
15
|
+
> Difference from `ctx.exit()`: `ctx.exit()` only terminates the current event flow; `ctx.exitAll()` terminates the current event flow and any **unexecuted** subsequent event flows in the same event dispatch.
|
|
16
16
|
|
|
17
|
-
## Type
|
|
17
|
+
## Type Definition
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
exitAll(): never;
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Calling `ctx.exitAll()` throws an internal `FlowExitAllException`, which the
|
|
23
|
+
Calling `ctx.exitAll()` throws an internal `FlowExitAllException`, which is caught by the FlowEngine to stop the current event flow instance and subsequent event flows under the same event. Once called, the remaining statements in the current JS code will not be executed.
|
|
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()` | Only terminates the current event flow; subsequent event flows are unaffected. |
|
|
30
|
+
| `ctx.exitAll()` | Terminates the current event flow and aborts subsequent event flows executed **sequentially** under the same event. |
|
|
31
31
|
|
|
32
|
-
## Execution
|
|
32
|
+
## Execution Mode
|
|
33
33
|
|
|
34
|
-
- **Sequential**: flows
|
|
35
|
-
- **Parallel**: flows
|
|
34
|
+
- **Sequential Execution**: Event flows under the same event are executed in order. After any event flow calls `ctx.exitAll()`, subsequent event flows will not execute.
|
|
35
|
+
- **Parallel Execution**: Event flows under the same event are executed in parallel. Calling `ctx.exitAll()` in one event flow will not interrupt other concurrent event flows (as they are independent).
|
|
36
36
|
|
|
37
37
|
## Examples
|
|
38
38
|
|
|
39
|
-
###
|
|
39
|
+
### Terminate all event flows when permission validation fails
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
+
// Abort the main event flow and subsequent event flows when permissions are insufficient
|
|
42
43
|
if (!hasPermission(ctx)) {
|
|
43
|
-
ctx.notification.error({ message: 'No permission' });
|
|
44
|
+
ctx.notification.error({ message: 'No operation permission' });
|
|
44
45
|
ctx.exitAll();
|
|
45
46
|
}
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
###
|
|
49
|
+
### Terminate when global pre-validation fails
|
|
49
50
|
|
|
50
51
|
```ts
|
|
52
|
+
// Example: If associated data is found to be non-deletable before deletion, prevent the main event flow and subsequent actions
|
|
51
53
|
const canDelete = await checkDeletable(ctx.model?.getValue?.());
|
|
52
54
|
if (!canDelete) {
|
|
53
|
-
ctx.message.error('Cannot delete:
|
|
55
|
+
ctx.message.error('Cannot delete: associated data exists');
|
|
54
56
|
ctx.exitAll();
|
|
55
57
|
}
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
###
|
|
60
|
+
### Choosing between ctx.exit() and ctx.exitAll()
|
|
59
61
|
|
|
60
62
|
```ts
|
|
61
|
-
// Only
|
|
63
|
+
// Only the current event flow needs to exit -> Use ctx.exit()
|
|
62
64
|
if (!params.valid) {
|
|
63
|
-
ctx.message.error('Invalid
|
|
64
|
-
ctx.exit();
|
|
65
|
+
ctx.message.error('Invalid parameters');
|
|
66
|
+
ctx.exit(); // Subsequent event flows are unaffected
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
//
|
|
69
|
+
// Need to terminate all subsequent event flows under the current event -> Use ctx.exitAll()
|
|
68
70
|
if (!ctx.model?.context?.getPermission?.()) {
|
|
69
|
-
ctx.notification.warning({ message: '
|
|
70
|
-
ctx.exitAll();
|
|
71
|
+
ctx.notification.warning({ message: 'Insufficient permissions' });
|
|
72
|
+
ctx.exitAll(); // Both the main event flow and subsequent event flows under the same event are terminated
|
|
71
73
|
}
|
|
72
74
|
```
|
|
73
75
|
|
|
74
|
-
###
|
|
76
|
+
### Prompt before terminating
|
|
75
77
|
|
|
76
78
|
```ts
|
|
77
79
|
if (!isValidInput(ctx.form?.getValues?.())) {
|
|
78
|
-
ctx.message.warning('Please
|
|
80
|
+
ctx.message.warning('Please correct the errors in the form first');
|
|
79
81
|
ctx.exitAll();
|
|
80
82
|
}
|
|
81
83
|
```
|
|
82
84
|
|
|
83
85
|
## Notes
|
|
84
86
|
|
|
85
|
-
- After `ctx.exitAll()`,
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- In parallel mode, `ctx.exitAll()` only
|
|
87
|
+
- After calling `ctx.exitAll()`, subsequent code in the current JS will not execute. It is recommended to explain the reason to the user via `ctx.message`, `ctx.notification`, or a modal before calling it.
|
|
88
|
+
- Business code usually does not need to catch `FlowExitAllException`; let the FlowEngine handle it.
|
|
89
|
+
- If you only need to stop the current event flow without affecting subsequent ones, use `ctx.exit()`.
|
|
90
|
+
- In parallel mode, `ctx.exitAll()` only terminates the current event flow and does not interrupt other concurrent event flows.
|
|
89
91
|
|
|
90
92
|
## Related
|
|
91
93
|
|
|
92
|
-
- [ctx.exit()](./exit.md):
|
|
93
|
-
- [ctx.message](./message.md):
|
|
94
|
-
- [ctx.modal](./modal.md):
|
|
94
|
+
- [ctx.exit()](./exit.md): Terminates only the current event flow
|
|
95
|
+
- [ctx.message](./message.md): Message prompts
|
|
96
|
+
- [ctx.modal](./modal.md): Confirmation modal
|
|
@@ -1,86 +1,89 @@
|
|
|
1
1
|
# ctx.exit()
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Terminates the execution of the current event flow; subsequent steps will not run. It is commonly used when business conditions are not met, the user cancels, or an irrecoverable error occurs.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
|
-
`ctx.exit()` is used in contexts
|
|
7
|
+
`ctx.exit()` is generally used in the following contexts where JS can be executed:
|
|
8
8
|
|
|
9
9
|
| Scenario | Description |
|
|
10
|
-
|
|
11
|
-
| **Event
|
|
12
|
-
| **Linkage
|
|
13
|
-
| **Action
|
|
10
|
+
|------|------|
|
|
11
|
+
| **Event Flow** | In event flows triggered by form submissions, button clicks, etc., terminates subsequent steps when conditions are not met. |
|
|
12
|
+
| **Linkage Rules** | In field linkages, filter linkages, etc., terminates the current event flow when validation fails or execution needs to be skipped. |
|
|
13
|
+
| **Action Events** | In custom actions (e.g., delete confirmation, pre-save validation), exits when the user cancels or validation fails. |
|
|
14
14
|
|
|
15
|
-
> Difference from `ctx.exitAll()`: `ctx.exit()` only
|
|
15
|
+
> Difference from `ctx.exitAll()`: `ctx.exit()` only terminates the current event flow; other event flows under the same event are not affected. `ctx.exitAll()` terminates the current event flow as well as any subsequent event flows under the same event that have not yet been executed.
|
|
16
16
|
|
|
17
|
-
## Type
|
|
17
|
+
## Type Definition
|
|
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 is caught by the FlowEngine to stop the current event flow execution. Once called, the remaining statements in the current JS code will not execute.
|
|
24
24
|
|
|
25
25
|
## Comparison with ctx.exitAll()
|
|
26
26
|
|
|
27
|
-
| Method | Scope |
|
|
28
|
-
|
|
29
|
-
| `ctx.exit()` |
|
|
30
|
-
| `ctx.exitAll()` |
|
|
27
|
+
| Method | Scope of Effect |
|
|
28
|
+
|------|----------|
|
|
29
|
+
| `ctx.exit()` | Terminates only the current event flow; subsequent event flows are unaffected. |
|
|
30
|
+
| `ctx.exitAll()` | Terminates the current event flow and aborts subsequent event flows under the same event that are set to **execute sequentially**. |
|
|
31
31
|
|
|
32
32
|
## Examples
|
|
33
33
|
|
|
34
|
-
### Exit on
|
|
34
|
+
### Exit on User Cancellation
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
|
+
// In a confirmation modal, terminate the event flow if the user clicks cancel
|
|
37
38
|
if (!confirmed) {
|
|
38
|
-
ctx.message.info('
|
|
39
|
+
ctx.message.info('Operation cancelled');
|
|
39
40
|
ctx.exit();
|
|
40
41
|
}
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
### Exit on
|
|
44
|
+
### Exit on Parameter Validation Failure
|
|
44
45
|
|
|
45
46
|
```ts
|
|
47
|
+
// Prompt and terminate when validation fails
|
|
46
48
|
if (!params.value || params.value.length < 3) {
|
|
47
|
-
ctx.message.error('Invalid
|
|
49
|
+
ctx.message.error('Invalid parameters, length must be at least 3');
|
|
48
50
|
ctx.exit();
|
|
49
51
|
}
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
### Exit
|
|
54
|
+
### Exit When Business Conditions Are Not Met
|
|
53
55
|
|
|
54
56
|
```ts
|
|
57
|
+
// Terminate if conditions are not met; subsequent steps will not execute
|
|
55
58
|
const record = ctx.model?.getValue?.();
|
|
56
59
|
if (!record || record.status !== 'draft') {
|
|
57
|
-
ctx.notification.warning({ message: 'Only
|
|
60
|
+
ctx.notification.warning({ message: 'Only drafts can be submitted' });
|
|
58
61
|
ctx.exit();
|
|
59
62
|
}
|
|
60
63
|
```
|
|
61
64
|
|
|
62
|
-
###
|
|
65
|
+
### Choosing Between ctx.exit() and ctx.exitAll()
|
|
63
66
|
|
|
64
67
|
```ts
|
|
65
|
-
// Only
|
|
68
|
+
// Only the current event flow needs to exit → Use ctx.exit()
|
|
66
69
|
if (!params.valid) {
|
|
67
|
-
ctx.message.error('Invalid
|
|
68
|
-
ctx.exit();
|
|
70
|
+
ctx.message.error('Invalid parameters');
|
|
71
|
+
ctx.exit(); // Other event flows are unaffected
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
//
|
|
74
|
+
// Need to terminate all subsequent event flows under the current event → Use ctx.exitAll()
|
|
72
75
|
if (!ctx.model?.context?.getPermission?.()) {
|
|
73
|
-
ctx.notification.warning({ message: '
|
|
74
|
-
ctx.exitAll();
|
|
76
|
+
ctx.notification.warning({ message: 'Insufficient permissions' });
|
|
77
|
+
ctx.exitAll(); // Both the current event flow and subsequent event flows under the same event are terminated
|
|
75
78
|
}
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
### Exit
|
|
81
|
+
### Exit Based on User Choice After Modal Confirmation
|
|
79
82
|
|
|
80
83
|
```ts
|
|
81
84
|
const ok = await ctx.modal?.confirm?.({
|
|
82
|
-
title: 'Confirm
|
|
83
|
-
content: '
|
|
85
|
+
title: 'Confirm Delete',
|
|
86
|
+
content: 'This action cannot be undone. Do you want to continue?',
|
|
84
87
|
});
|
|
85
88
|
if (!ok) {
|
|
86
89
|
ctx.message.info('Cancelled');
|
|
@@ -90,12 +93,12 @@ if (!ok) {
|
|
|
90
93
|
|
|
91
94
|
## Notes
|
|
92
95
|
|
|
93
|
-
- After `ctx.exit()`,
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
+
- After calling `ctx.exit()`, subsequent code in the current JS will not execute; it is recommended to explain the reason to the user via `ctx.message`, `ctx.notification`, or a modal before calling it.
|
|
97
|
+
- There is usually no need to catch `FlowExitException` in business code; let the FlowEngine handle it.
|
|
98
|
+
- If you need to terminate all subsequent event flows under the current event, use `ctx.exitAll()`.
|
|
96
99
|
|
|
97
100
|
## Related
|
|
98
101
|
|
|
99
|
-
- [ctx.exitAll()](./exit-all.md):
|
|
100
|
-
- [ctx.message](./message.md):
|
|
101
|
-
- [ctx.modal](./modal.md):
|
|
102
|
+
- [ctx.exitAll()](./exit-all.md): Terminates the current event flow and subsequent event flows under the same event.
|
|
103
|
+
- [ctx.message](./message.md): Message prompts.
|
|
104
|
+
- [ctx.modal](./modal.md): Confirmation modals.
|