Node

Nodes are defined through FlowNodeEntity

Node Core API

  • id: string Node id
  • flowNodeType: string | number Node type
  • bounds: Rectangle Get the node's x, y, width, height, equivalent to transform.bounds
  • blocks: FlowNodeEntity[] Get child nodes, including collapsed child nodes
  • parent: FlowNodeEntity | undefined Get the parent node (Such as loop)
  • document: WorkflowDocument Document link
  • transform: FlowNodeTransformData Get the node's transform data
  • renderData: FlowNodeRenderData Get the node's render data, including render status
  • form: NodeFormProps Get the node's form data, like getNodeForm
  • scope: FlowNodeScope Get the node's variable public scope
  • privateScope: FlowNodeScope Get the node's variable private scope
  • lines: WorkflowNodeLinesData Get the node's lines data
  • ports: WorkflowNodePortsData Get the node's ports data
  • getNodeRegistry(): WorkflowNodeRegistry Get the node's registry
  • getService(): Get the IOC Service, such asnode.getService(HistoryService)
  • getExtInfo(): Get the node's ext info, such as node.getExtInfo<{ test: string }>()
  • updateExtInfo(): Update the node's ext info, such as node.updateExtInfo<{ test: string }>({ test: 'test' })
  • dispose(): Destroy node
  • toJSON(): Get the node's json data

Node Data

Can be obtained through node.toJSON()

Basic Structure:
  • id: string Unique identifier for the node, must be unique
  • meta: object Node's UI configuration information, such as position information for free layout
  • type: string | number Node type, corresponds to type in nodeRegistries
  • data: object Node form data, can be customized by business
  • blocks: array Node branches, using block is more suitable for Gramming free layout scenarios, used in sub-nodes of sub-canvas
  • edges: array Edge data of sub-canvas
const nodeData: FlowNodeJSON = {
  id: 'xxxx',
  type: 'condition',
  data: {
    title: 'MyCondition',
    desc: 'xxxxx'
  },
}

Node Definition

In free layout scenarios, node definition is used to declare node's initial position/size, ports, form rendering, etc. For details, see Declare Node

Get Current Rendering Node

Get node-related methods through useNodeRender

function BaseNode() {
  const { id, type, data, updateData, node } = useNodeRender()
}

Get Input/Output Nodes or Lines for Current Node

// get input nodes (calculated through connection lines)
node.lines.inputNodes
// get all input nodes (recursively gets all upstream nodes)
node.lines.allInputNodes
// get output nodes
node.lines.outputNodes
// get all output nodes
node.lines.allOutputNodes
// input lines (contains the line that isDrawing or isHidden)
node.lines.inputLines
// output lines (contains the line that isDrawing or isHidden)
node.lines.outputLines
// all availableLines (Doesn't contain the lines that isDrawing or isHidden)
node.lines.availableLines

Create Node

const ctx = useClientContext()

ctx.document.createWorkflowNode({
  id: 'xxx', // Must be unique within the canvas
  type: 'custom',
  meta: {
    /**
     * If not provided, defaults to creating in the center of the canvas
     * To get position from mouse position (e.g., creating node by clicking anywhere on canvas),
     * convert using `ctx.playground.config.getPosFromMouseEvent(mouseEvent)`
     */
    position: { x: 100, y: 100 } //
  },
  data: {}, // Form-related data
  blocks: [], // Sub-canvas nodes
  edges: [] // Sub-canvas edges
})
const dragService = useService<WorkflowDragService>(WorkflowDragService);

// mouseEvent here will automatically convert to canvas position
dragService.startDragCard(nodeType, mouseEvent, {
  id: 'xxxx',
  data: {}, // Form-related data
  blocks: [], // Sub-canvas nodes
  edges: [] // Sub-canvas edges
})

Delete Node

Delete node through node.dispose

function BaseNode({ node }) {
  function onClick() {
    node.dispose()
  }
  return (
    <button onClick={onClick}>Delete</button>
  )
}

Update Node Data

function BaseNode() {
  const { form, node } = useNodeRender();
  // Corresponds to node's data
  // 1. form.values: Corresponds to node's data
  // 2. form.setValueIn('title', 'xxxx'): Update data.title
  // 3. form.getValueIn('title'): Get data.title
  // 4. form.updateFormValues({ ... }) Update all form values

  function onChange(e) {
    form.setValueIn('title', e.target.value)
  }
  return <input value={form.values.title} onChange={onChange}/>
}
  • Update form data through Field, see details in Form Usage

function FormRender() {
  return (
    <Field name="title">
      <Input />
    </Field>
  )
}

Update Node's extInfo Data

extInfo is used to store UI states, if node engine is not enabled, node's data will be stored in extInfo by default

function BaseNode({ node }) {
  const times = node.getExtInfo()?.times || 0
  function onClick() {
    node.updateExtInfo({ times: times ++ })
  }
  return (
    <div>
      <span>Click Times: {times}</span>
      <button onClick={onClick}>Click</button>
    </div>
  )
}