输出变量
在 Flowgram 中,变量是连接各个节点的关键枢纽。一个节点的输出,往往是另一个节点的输入。因此,如何优雅地定义和输出变量,就成了一门必修课。本文将带你探索在 Flowgram 中输出变量的各种姿势。
我们主要将变量分为两类:
- 节点变量:作用域仅限于单个节点,通常作为该节点的产出,供后续节点使用。
- 全局变量:作用域贯穿整个流程,任何节点都可以读取和修改,适合存放一些公共状态或配置。
输出节点变量
输出节点变量,意味着这个变量和当前节点的生命周期是绑定的。当节点被创建 时,变量就诞生了;当节点被删除时,变量也随之消逝。我们通常有两种方式来输出节点变量:
- 通过表单配置:在节点的设置面板(Form)中,通过一些预设的配置或自定义的逻辑来声明输出变量。这种方式非常直观,所见即所得。
- 通过 API 调用:在插件(Plugin)中,通过调用
getNodeScope
API 来动态地为节点添加、修改或删除变量。这种方式更加灵活,适合处理复杂的、动态的业务场景。
接下来,我们将深入探讨这两种方式的具体用法。
方式一:通过表单配置输出
在节点的 form-meta.ts
文件中进行配置,是定义节点输出变量最常见的方式。它又可以分为两种玩法:一种是“懒人版”,使用我们内置的物料;另一种是“大神版”,完全自定义。
provideJsonSchemaOutputs
物料
如果你的节点需要输出的变量,其结构恰好和表单的 JSON Schema
结构一致,那么恭喜你,provideJsonSchemaOutputs
这个副作用(Effect)物料就是为你量身定做的!它就像一个自动化的 “变量搬运工”,能自动将表单中的数据,原封不动地转换为节点的输出变量。
使用起来非常简单,只需要在 formMeta
的 effect
中加上两行配置即可:
form-meta.ts
import {
syncVariableTitle,
provideJsonSchemaOutputs,
} from '@flowgram.ai/form-materials';
export const formMeta = {
effect: {
title: syncVariableTitle, // 变量标题自动同步
outputs: provideJsonSchemaOutputs,
},
};
通过表 单副作用自定义输出
provideJsonSchemaOutputs
虽然方便,但它只适配 JsonSchema。
如果你想要定义自己的一套 Schema,那么你就需要自定义表单的副作用了。
Flowgram 提供了一个强大的辅助函数 createEffectFromVariableProvider
,它能帮你轻松地创建一个用于提供变量的副作用。你可以把它想象成一个“变量加工厂”,输入的是表单数据,输出的是你精心加工后的变量。
下面这个例子中,我们为表单的两个字段 path.to.value
和 path.to.value2
分别创建了输出变量。当用户在表单中填写这两个字段时,parse
函数就会被触发,将用户输入的值(v
)转换成一个标准的变量声明对象。
node-registries.ts
import {
FlowNodeRegistry,
createEffectFromVariableProvider,
ASTFactory,
type ASTNodeJSON
} from '@flowgram.ai/fixed-layout-editor';
export function createTypeFromValue(value: string): ASTNodeJSON | undefined {
switch (value) {
case 'string':
return ASTFactory.createString();
case 'number':
return ASTFactory.createNumber();
case 'boolean':
return ASTFactory.createBoolean();
case 'integer':
return ASTFactory.createInteger();
default:
return;
}
}
export const nodeRegistries: FlowNodeRegistry[] = [
{
type: 'start',
formMeta: {
effect: {
// Create first variable
// = variableData.setVar('path.to.value', ASTFactory.createVariableDeclaration(parse(v)))
'path.to.value': createEffectFromVariableProvider({
// parse form value to variable
parse(v: string) {
return {
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key_${node.id}`,
type: createTypeFromValue(v)
}
}
}),
// Create second variable
// = variableData.setVar('path.to.value2', ASTFactory.createVariableDeclaration(parse(v)))
'path.to.value2': createEffectFromVariableProvider({
// parse form value to variable
parse(v: string) {
return {
meta: {
title: `Your Output Variable Title 2`,
},
key: `your_variable_global_unique_key_${node.id}_2`,
type: createTypeFromValue(v)
}
}
}),
},
render: () => (
// ...
)
},
}
]
方式二:使用 getNodeScope
API
除了在表单中静态配置,我们还可以在插件(Plugin)中,通过 getNodeScope
API 来获取作用域,从而动态地操作节点的变量。
这种方式赋予了你极高的自由度,你可以在任何时机、任何地点,对节点的变量进行增、删、改、查。
getNodeScope
会返回一个节点的变量作用域(Scope)对象,这个对象上挂载了几个核心方法:
setVar(variable)
: 设置一个变量。
setVar(namespace, variable)
: 在指定的命名空间下设置一个变量。
getVar()
: 获取所有变量。
getVar(namespace)
: 获取指定命名空间下的变量。
clearVar()
: 清空所有变量。
clearVar(namespace)
: 清空指定命名空间下的变量。
下面的例子演示了如何在插件的 onInit
生命周期中,获取开始节点的 Scope
,并对它的变量进行一系列操作。
sync-variable-plugin.tsx
import {
FlowDocument,
definePluginCreator,
PluginCreator,
getNodeScope,
} from '@flowgram.ai/fixed-layout-editor';
export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions> =
definePluginCreator<SyncVariablePluginOptions, FixedLayoutPluginContext>({
onInit(ctx, options) {
const startNode = ctx.get(FlowDocument).getNode('start_0');
const startScope = getNodeScope(startNode)
// 1. Set Variable For Start Scope
startScope.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_unique_key`,
type: ASTFactory.createString(),
})
)
// 2. Create, Update, Read, Delete Variable in namespace_2
startScope.setVar(
'namespace_2',
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title 2`,
},
key: `your_variable_global_unique_key_2`,
type: ASTFactory.createString(),
})
)
console.log(startScope.getVar('namespace_2'))
// 3. Delete Variable in namespace_2
startScope.clearVar('namespace_2')
}
})
输出全局变量
全局变量就像是整个流程的“共享内存”,任何节点、任何插件都可以访问和修改它。它非常适合用来存储一些贯穿始终的状态,比如用户信息、环境配置等等。
和节点变量类似,我们也有两种主要的方式来获取全局变量的作用域(GlobalScope
)。
方式一:在插件中获取
在插件的上下文中 (ctx
),我们可以直接“注入”GlobalScope
的实例:
global-variable-plugin.tsx
import {
GlobalScope,
definePluginCreator,
PluginCreator
} from '@flowgram.ai/fixed-layout-editor';
export const createGlobalVariablePlugin: PluginCreator<SyncVariablePluginOptions> =
definePluginCreator<SyncVariablePluginOptions, FixedLayoutPluginContext>({
onInit(ctx, options) {
const globalScope = ctx.get(GlobalScope)
globalScope.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key`,
type: ASTFactory.createString(),
})
)
}
})
方式二:在 React 组件中获取
如果你想在画布的 React 组件中与全局变量交互,可以使用 useService
这个 Hook 来获取 GlobalScope
的实例:
global-variable-component.tsx
import {
GlobalScope,
useService,
} from '@flowgram.ai/fixed-layout-editor';
function GlobalVariableComponent() {
const globalScope = useService(GlobalScope)
// ...
const handleChange = (v: string) => {
globalScope.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key_${v}`,
type: ASTFactory.createString(),
})
)
}
return <Input onChange={handleChange}/>
}
全局作用域的 API
GlobalScope
的 API 设计得和节点作用域(NodeScope
)几乎一模一样,都提供了 setVar
、getVar
、clearVar
等方法,并且同样支持命名空间(namespace)。这种一致性设计大大降低了我们的学习成本。
下面是一个在插件中操作全局变量的综合示例:
sync-variable-plugin.tsx
import {
GlobalScope,
} from '@flowgram.ai/fixed-layout-editor';
// ...
onInit(ctx, options) {
const globalScope = ctx.get(GlobalScope);
// 1. Create, Update, Read, Delete Variable in GlobalScope
globalScope.setVar(
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title`,
},
key: `your_variable_global_unique_key`,
type: ASTFactory.createString(),
})
)
console.log(globalScope.getVar())
globalScope.clearVar()
// 2. Create, Update, Read, Delete Variable in GlobalScope's namespace: 'namespace_1'
globalScope.setVar(
'namespace_1',
ASTFactory.createVariableDeclaration({
meta: {
title: `Your Output Variable Title 2`,
},
key: `your_variable_global_unique_key_2`,
type: ASTFactory.createString(),
})
)
console.log(globalScope.getVar('namespace_1'))
globalScope.clearVar('namespace_1')
// ...
}
详见:Class: GlobalScope