Node Form | Specifically refers to forms within flow nodes or forms that expand when clicking nodes, associated with node data. |
Node Engine | One of FlowGram.ai's built-in engines, which primarily maintains node data CRUD operations and provides capabilities for rendering, validation, side effects, canvas/variable linkage, etc. Additionally, it provides capabilities for node error capture rendering, placeholder rendering when there's no content, as shown in the following chapter examples. |
formMeta
is the only configuration entry point for node forms, configured on each node's NodeRegistry.
In NodeRegistry
, we configure node forms through formMeta
, which follows the following API.
It's important to note that node forms differ significantly from general forms in that their data logic (such as validation, side effects after data changes, etc.) needs to remain effective even when the form is not rendered - we call this separation of data and rendering. Therefore, this data logic needs to be configured in non-render fields within formMeta, ensuring the node engine can call these logics even when not rendering. General form engines (like react-hook-form) don't have this restriction, and validation can be written directly in React components.
The render
field is used to configure form rendering logic
render: (props: FormRenderProps<any>) => React.ReactElement;
The returned React component can use the following form components and models:
Field
is a React higher-order component for form fields, encapsulating common form field logic such as data and state injection, component refresh, etc. Its core required parameter is name
, used to declare the form item's path, which must be unique within a form.
The rendering part of Field supports three writing methods, as follows:
Field
instance is usually passed through render props (as in above example), or obtained through useCurrentField
hook. It contains common APIs for form fields at the rendering level.
Note: Field
is a rendering model, only providing APIs that general components need, such as value
onChange
onFocus
onBlur
. For data-related APIs, please use the Form
model instance, such as form.setValueIn(name, value)
to set a field's value.
FieldArray
is a React higher-order component for array type fields, encapsulating common logic for array type fields, such as data and state injection, component refresh, and array item iteration. Its core required parameter is name
, used to declare the form item's path, which must be unique within a form.
Basic usage of FieldArray
can be found in the following example:
FieldArray
inherits from Field
, it's the rendering level model for array type fields. Besides common rendering level APIs, it also includes basic array operations like FieldArray.map
, FieldArray.remove
, FieldArray.append
, etc. API usage can also be found in the above array example.
Form
component is the outermost higher-order component for forms. The above capabilities like Field
FieldArray
can only be used under this higher-order component. Node form rendering has already encapsulated <Form />
inside the engine, so users don't need to care about it and can directly use Field
in the React component returned by render
. However, if users need to use the form engine independently or render a form independently outside the node, they need to wrap the form content with the Form
component themselves.
Form
instance can be obtained through the input parameters of the render
function, or through the useForm
hook, see example. It is the core model facade of the form, through which users can manipulate form data, listen to changes, trigger validation, etc.
Based on the "separation of data and rendering" concept mentioned in the FormMeta section, validation logic needs to be configured globally in FormMeta
, and declared through path matching to act on form items, as shown in the following example.
Paths support fuzzy matching, see Paths section.
ValidateTrigger.onChange | Validate when form data changes (not including initialization data) |
ValidateTrigger.onBlur | Validate when form item input control onBlur. Note, there are two prerequisites: first, the form item's input control needs to support the onBlur parameter, second, Field.onBlur needs to be passed to that control: <Field>{({field})=><Input ... onBlur={field.onBlur}>}</Field> |
It's recommended to configure validateTrigger
as ValidateTrigger.onChange
i.e., validate when data changes. If configured as ValidateTrigger.onBlur
, validation will only trigger when the component blur event triggers. When the node form is not rendering, even if the data changes, validation won't trigger.
If name
is not passed, it defaults to getting the validate
of the Field
under the current <Field />
tag. By passing name
, you can get any Field
's validate under <Form />
.
.
as level separator, e.g., a.b.c
points to 1
under data {a:{b:{c:1}}}
Note: * only represents drilling down one level
arr.* | All first-level sub-items of arr field |
arr.x.* | All first-level sub-items of arr.x |
arr.*.x | x under all first-level sub-items of arr |
Side effects are a concept unique to node forms, referring to side effects that need to be executed when node data changes. Similarly, following the principle of "separation of data and rendering", side effects, like validation, are also configured globally in FormMeta
.
DataEvent.onValueChange | Triggered when data changes |
DataEvent.onValueInit | Triggered when data initializes |
DataEvent.onValueInitOrChange | Triggered both during data initialization and changes |
The following hooks can be used inside node forms
() => Field
This hook needs to be used inside Field tags
() => FieldState
This hook needs to be used inside Field tags
(name?: FieldName) => () => Promise<void>
If you need to actively trigger field validation, you can use this hook to get the Field's validate function.
name
is the Field's path, if not passed it defaults to getting the validate of the current <Field />
() => Form
Used to get Form instance.
Note, this hook doesn't work in the first layer of the render
function, it can only be used inside React components within the render
function. The render
function's input parameters already include form: Form
, which can be used directly.
props.form
in render function's first layeruseForm
inside components<TValue = FieldValue>(name: FieldName) => TValue
This hook is similar to the above useForm
, it doesn't work in the first layer of the render
function, only usable inside wrapped components. If you need to use it at the render
root level, you can wrap the returned content in a component layer.
The following hooks are used outside node forms, such as on the canvas globally or on adjacent nodes to monitor a node form's data or state. Usually needs to pass node: FlowNodeEntity
as a parameter
Monitor the values of the entire form inside the node
<TFormValues = any>(node: FlowNodeEntity) => TFormValues | undefined
Monitor the value of a specific form item inside the node
<TValue = any>(node: FlowNodeEntity,name: string) => TFormValues | undefined
Monitor the form state inside the node
(node: FlowNodeEntity) => FormState | undefined
Monitor the form Errors inside the node
(node: FlowNodeEntity) => Errors | undefined
Monitor the form Warnings inside the node
(node: FlowNodeEntity) => Warnings | undefined