fcode-forms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFactorial Code — Forms
Factorial Code — 表单
A Factorial Code Form embeds a process's input-parameter form on any webpage.
Each submission starts a process execution with the form data, and the result is
handled in-page (messages, redirects, callbacks). For the schema itself, see
.
fcode-json-schemaFactorial Code表单可将流程的输入参数表单嵌入到任意网页中。每次提交都会使用表单数据启动流程执行,结果会在页面内处理(消息、重定向、回调)。关于架构本身,请参阅。
fcode-json-schemaGotchas
注意事项
- The form is the process's — there is no separate form definition. To change fields/validation/labels, edit the schema, not the embed code.
parametersSchema.json - and
team/processare both mandatory on every embed, and both take slugs — not the per-workspace UUIDs.processId - Always pin the embed to the alias (
stable/data-fcode-form-process-version="stable"). An unpinned form runs the current version, so everyprocessVersion: "stable"changes it immediately. Details below.fcode push - An unknown version or alias doesn't fail the form — it silently runs the current version (see "Pin the form to a version").
- The flag must be enabled — on the process Dashboard, or via
Formsin the process's"form": { "enabled": true }+metadata.json— or the embed won't render.fcode push - A new form requires a Factorial user by default (). An embed on a public page needs
authMode: FACTORIAL, or every request gets aauthMode: NONE(below).401 - A schema can't carry executable JavaScript. and field
embedFormOptions.onChangewere removed, a returnedtransformFnis ignored, and authored HTML is sanitized. Client-side behaviour lives in the embedding page.jsCallback - Never put secrets in embed code or — they run in the browser.
options - Form text is translated with tokens in the schema, substituted server-side before the schema is served. See
fcode.i18n("key").fcode-i18n - Form submissions run under a request timeout (about a minute) — keep the synchronous process fast, or run long work asynchronously (see below).
- Prefer driving UX from the process return value (below); reserve
/
onSuccessfor client-only logic.onError
- 表单即流程的——没有单独的表单定义。要更改字段/验证/标签,请编辑架构,不要修改嵌入代码。
parametersSchema.json - 和
team/process在所有嵌入中都是必填项,且两者均使用slug——而非每个工作区的UUID。processId - 始终将嵌入固定到别名(
stable/data-fcode-form-process-version="stable")。未固定版本的表单会运行当前版本,因此每次processVersion: "stable"都会立即更改它。详情见下文。fcode push - 未知版本或别名不会导致表单失败——它会静默运行当前版本(请参阅“将表单固定到版本”)。
- 必须启用标志——在流程仪表板上,或通过流程的
Forms中的metadata.json+"form": { "enabled": true }——否则嵌入将无法渲染。fcode push - 默认情况下,新表单需要Factorial用户()。公共页面上的嵌入需要设置
authMode: FACTORIAL,否则每个请求都会返回authMode: NONE(见下文)。401 - 架构不能包含可执行JavaScript。和字段
embedFormOptions.onChange已被移除,返回的transformFn会被忽略,编写的HTML会被清理。客户端行为由嵌入页面控制。jsCallback - 切勿在嵌入代码或中放入机密信息——它们会在浏览器中运行。
options - 表单文本通过架构中的令牌进行翻译,在架构被提供前由服务器端替换。请参阅
fcode.i18n("key")。fcode-i18n - 表单提交受请求超时限制(约一分钟)——保持同步流程快速,或将长时间任务异步运行(见下文)。
- 优先通过流程返回值驱动用户体验(见下文);将/
onSuccess保留用于客户端专属逻辑。onError
Enable a form
启用表单
- Create the process and define its input parameters (these become the fields).
- Enable the flag — either on the process Dashboard, or from the CLI workspace in
Forms, thenprocesses/<slug>/metadata.json:fcode push
json
{
"name": "Contact request",
"tags": [],
"form": { "enabled": true }
}For marketplace app processes, also takes an optional
( | | | ) marking
the process's role in the app. Field reference in .
form"appRole"INSTALLSETTINGSUSER_FACING_FORMUNINSTALLfcode-cliAn or form is re-opened after the app is already configured,
so it should show the current values rather than an empty form. Add a
that reads them from team variables and the datastore — and
never echo a stored secret back, only whether one is set. Pattern and code in
.
INSTALLSETTINGSpreRenderProcessreferences/advanced.mdRead submitted values in process code like any parameters:
const { context: { parameters } } = fcode;- 创建流程并定义其输入参数(这些将成为表单字段)。
- 启用标志——可以在流程仪表板上操作,或在CLI工作区的
Forms中设置,然后执行processes/<slug>/metadata.json:fcode push
json
{
"name": "联系请求",
"tags": [],
"form": { "enabled": true }
}对于市场应用流程,还可以接受可选的( | | | ),用于标记流程在应用中的角色。字段参考请见。
form"appRole"INSTALLSETTINGSUSER_FACING_FORMUNINSTALLfcode-cliINSTALLSETTINGSpreRenderProcessreferences/advanced.md在流程代码中像读取其他参数一样读取提交的值:
const { context: { parameters } } = fcode;Restrict who can open the form
限制可打开表单的用户
The field next to the flag ( in
) decides who may read the form schema and submit it:
AuthenticationFormsform.authModemetadata.json | Who gets in |
|---|---|
| Only Factorial users of the company that installed the app. Every request must carry a Factorial-issued user token in the |
| Anyone who knows the form URL can open and submit it |
- New forms are created requiring a Factorial user. Forms enabled before this field existed keep behaving as public forms until you change them.
- Forms embedded inside Factorial (the marketplace /
INSTALL/SETTINGS/USER_FACING_FORMscreens) send the token for you — this is whatUNINSTALLis for.FACTORIAL - A protected form is still openable from the playground link on the process
Dashboard: the playground sends the developer's own Factorial Code token as
and access is granted through workspace membership.
Fcode-Platform-Token
json
{
"name": "Connect your account",
"form": { "enabled": true, "authMode": "FACTORIAL", "appRole": "INSTALL" }
}Public forms carry no entry. To lift protection, set
explicitly — omitting the field leaves the form
protected. Field reference in .
authMode"authMode": "NONE"fcode-cliFormsAuthenticationmetadata.jsonform.authMode | 允许访问的用户 |
|---|---|
| 仅安装应用的公司的Factorial用户。每个请求必须在 |
| 任何知道表单URL的人都可以打开并提交表单 |
- 新创建的表单默认要求Factorial用户访问。在此字段存在之前启用的表单会保持公共表单的行为,直到您更改设置。
- 嵌入Factorial内部的表单(市场的/
INSTALL/SETTINGS/USER_FACING_FORM屏幕)会自动发送令牌——这就是UNINSTALL模式的用途。FACTORIAL - 受保护的表单仍可通过流程仪表板上的沙箱链接打开:沙箱会将开发者自己的Factorial Code令牌作为发送,并通过工作区成员身份授予访问权限。
Fcode-Platform-Token
json
{
"name": "连接您的账户",
"form": { "enabled": true, "authMode": "FACTORIAL", "appRole": "INSTALL" }
}公共表单不包含条目。要取消保护,请显式设置——省略该字段会保持表单的保护状态。字段参考请见。
authMode"authMode": "NONE"fcode-cliEmbed a form
嵌入表单
Two mandatory inputs, both slugs, plus the version pin you should always
add:
- — from
fcode-team-slughttps://code.factorial.dev/platform/<fcode-team-slug> - — the Slug field on the process Dashboard (e.g.
fcode-process-slug)send-welcome-email - process version — pin it to the alias (next section)
stable
Use the slug, not the process ID. A process ID is a UUID that differs per
workspace, so an id-based embed breaks when the snippet moves between workspaces
(staging → production, or a customer's deploy workspace); with slugs, only the
team slug changes. Existing id-based embeds keep working — the API resolves
either — and the React prop is still named , but feed it a slug.
processIdThe Slug field is editable and there is no redirect for the old value, so
renaming a process's slug breaks every embed already pasted into a page (the
same exposure webhooks have). Settle the slug before handing out embed code.
Load the SDK once (needed for the data-attribute and methods):
Fcode.initFormhtml
<script defer src="https://code.factorial.dev/sdk/forms.js"></script>Method 1 — data attributes (SDK replaces the element):
html
<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-process-version="stable"
></div>Method 2 — (selector or DOM element):
Fcode.initFormhtml
<div id="my-fcode-form"></div>
<script>
Fcode.initForm("#my-fcode-form", {
team: "<fcode-team-slug>",
process: "<fcode-process-slug>",
processVersion: "stable",
});
</script>Method 3 — React component (React 17/18; install
):
FcodeForm@factorialco/fcode-react-formsjsx
import FcodeForm from "@factorialco/fcode-react-forms";
const MyComponent = () => (
<FcodeForm
team={"<fcode-team-slug>"}
processId={"<fcode-process-slug>"}
processVersion={"stable"}
/>
);In SSR frameworks (e.g. Next.js), import it dynamically with .
ssr: false两个必填输入项,均为slug,加上您应始终添加的版本固定:
- ——来自
fcode-team-slughttps://code.factorial.dev/platform/<fcode-team-slug> - ——流程仪表板上的Slug字段(例如
fcode-process-slug)send-welcome-email - 流程版本——固定到别名(下一节)
stable
使用slug而非流程ID。流程ID是每个工作区不同的UUID,因此基于ID的嵌入在代码片段在工作区之间移动时会失效( staging → production,或客户的部署工作区);使用slug的话,只有团队slug需要更改。现有的基于ID的嵌入仍可正常工作——API会解析两者——React属性仍命名为,但请传入slug。
processIdSlug字段可编辑,且旧值没有重定向,因此重命名流程的slug会破坏所有已粘贴到页面中的嵌入代码(与webhook的情况相同)。在分发嵌入代码前确定slug。
加载一次SDK(数据属性和方法需要):
Fcode.initFormhtml
<script defer src="https://code.factorial.dev/sdk/forms.js"></script>方法1 — 数据属性(SDK会替换元素):
html
<div
data-fcode-form-team="<fcode-team-slug>"
data-fcode-form-process="<fcode-process-slug>"
data-fcode-form-process-version="stable"
></div>方法2 — (选择器或DOM元素):
Fcode.initFormhtml
<div id="my-fcode-form"></div>
<script>
Fcode.initForm("#my-fcode-form", {
team: "<fcode-team-slug>",
process: "<fcode-process-slug>",
processVersion: "stable",
});
</script>方法3 — React组件(React 17/18;安装):
FcodeForm@factorialco/fcode-react-formsjsx
import FcodeForm from "@factorialco/fcode-react-forms";
const MyComponent = () => (
<FcodeForm
team={"<fcode-team-slug>"}
processId={"<fcode-process-slug>"}
processVersion={"stable"}
/>
);在SSR框架中(例如Next.js),使用动态导入。
ssr: falsePin the form to a version
将表单固定到版本
The version pin ( /
) takes a published process version tag () or a version
alias. Always pin to the alias — it always exists, points at the
workspace's stable version, and:
data-fcode-form-process-versionprocessVersionv1.0.0stable- Pushes never change live forms. An unpinned embed runs the current
version, so every hits it immediately. Pinned to
fcode push, the form only changes when a release moves the alias (from the web UI's team settings → Versions tab; CLI equivalents instable) — and rolling back is re-pointing the alias, without editing the embedded page.fcode-cli - Pinning to rather than a hardcoded tag follows the same portability logic as using slugs instead of IDs: the embed survives releases without edits.
stable
The version applies to both requests the form makes — loading the form
definition and submitting it. The process Dashboard writes the pin for you:
pick a version or alias in the selector next to the embed code and copy the
generated snippet.
An unknown version or alias falls back to the current one. The form still
loads and still submits — it runs the process's current version, and the
platform records only a server-side warning. That keeps a page you no longer
control working after a version is deleted, but a typo in the attribute is
silent: check the execution's version if a submission behaves unexpectedly.
Calling the form endpoints directly (not through the SDK)? They also accept a
query parameter, which takes precedence over the underlying
header — same as webhooks (see ).
version_tagFcode-Version-Tagfcode-cli版本固定( / )可接受已发布的流程版本标签()或版本别名。始终固定到别名——它始终存在,指向工作区的稳定版本,并且:
data-fcode-form-process-versionprocessVersionv1.0.0stable- 推送不会更改实时表单。未固定版本的嵌入会运行当前版本,因此每次都会立即影响它。固定到
fcode push后,只有当版本发布移动了别名时(从Web UI的团队设置→版本选项卡;CLI等效命令见stable),表单才会更改——回滚只需重新指向别名,无需编辑嵌入页面。fcode-cli - 固定到而非硬编码标签,与使用slug而非ID的可移植性逻辑相同:嵌入无需编辑即可在版本发布后继续工作。
stable
版本适用于表单发出的两个请求——加载表单定义和提交表单。流程仪表板会为您生成版本固定:在嵌入代码旁边的选择器中选择版本或别名,然后复制生成的代码片段。
未知版本或别名会回退到当前版本。表单仍会加载并提交——它会运行流程的当前版本,平台仅记录服务器端警告。这可以让您无法控制的页面在版本被删除后仍能工作,但属性中的拼写错误不会有提示:如果提交行为异常,请检查执行的版本。
直接调用表单端点(不通过SDK)?它们也接受查询参数,该参数优先于底层的头——与webhook相同(见)。
version_tagFcode-Version-Tagfcode-cliHandle submission results
处理提交结果
Default: a loading overlay shows during execution; on success the form is
replaced with a success message, on error an error message.
Callbacks (same shape across methods):
js
Fcode.initForm("#my-fcode-form", {
team: "<fcode-team-slug>",
process: "<fcode-process-slug>",
onSuccess: (formId, processExecutionResult, formSubmittedData) => {},
onError: (formId, error, formSubmittedData) => {},
});With data attributes, point to global functions via
,
and .
data-fcode-form-on-success="HANDLER_NAME"data-fcode-form-on-next-step="..."data-fcode-form-on-error="..."Drive behavior from the process return value (no client code needed):
js
return { message: "Thanks, <b>we received your request</b>." }; // success message (HTML allowed)
return { status: 400, body: { formErrors: { // inline validation errors
fields: { email: "Invalid email." }, global: ["A global error."] } } };
return { redirect: { url: "https://example.com", timeout: 2000 } }; // redirect after submitAuthored HTML is sanitized. Markup in (and in a schema's
blocks) renders, but tags, inline handlers and
URLs are stripped and never execute. A returned is ignored — put
behaviour in the success / next-step / error callbacks instead.
messagerawHtml<script>on*javascript:jsCallback默认行为:执行期间显示加载覆盖层;成功时表单会被替换为成功消息,错误时显示错误消息。
回调(所有方法的格式相同):
js
Fcode.initForm("#my-fcode-form", {
team: "<fcode-team-slug>",
process: "<fcode-process-slug>",
onSuccess: (formId, processExecutionResult, formSubmittedData) => {},
onError: (formId, error, formSubmittedData) => {},
});使用数据属性时,通过、和指向全局函数。
data-fcode-form-on-success="HANDLER_NAME"data-fcode-form-on-next-step="..."data-fcode-form-on-error="..."通过流程返回值驱动行为(无需客户端代码):
js
return { message: "感谢您的提交,<b>我们已收到您的请求</b>." }; // 成功消息(允许HTML)
return { status: 400, body: { formErrors: { // 内联验证错误
fields: { email: "无效的邮箱地址." }, global: ["全局错误."] } } };
return { redirect: { url: "https://example.com", timeout: 2000 } }; // 提交后重定向编写的HTML会被清理。中的标记(以及架构的块中的标记)会渲染,但标签、内联处理程序和 URL会被剥离且永远不会执行。返回的会被忽略——请将行为放在成功/下一步/错误回调中。
messagerawHtml<script>on*javascript:jsCallbackKeep it fast, or go async
保持快速,或转为异步
The submission waits for the process to finish, under a request timeout (about a
minute). Heavy work done inline — slow API calls, large exports, multi-record
syncs — will blow the timeout and fail the submit.
Go async when the work can be slow:
- Embed — the submission returns
async: true+ an execution ID immediately instead of waiting for the result (see201).references/advanced.md - Hand off to another process — kick off the heavy work with
(see
fcode.processes.run("process-identifier", options)/fcode-javascript) and return a quick acknowledgement (fcode-python/message) rather than awaiting it inline.redirect
Stay synchronous only when the request is genuinely fast, or when data must
flow between steps. For passing data, don't block the submit — instead:
- computes server-side
preRenderProcessbefore the form renders (seevariables).references/advanced.md - Multi-step forms carry state forward via +
nextProcessId(below).variables
提交会等待流程完成,受请求超时限制(约一分钟)。在同步流程中执行繁重工作——缓慢的API调用、大型导出、多记录同步——会触发超时并导致提交失败。
当工作可能较慢时转为异步:
- 嵌入——提交会立即返回
async: true+ 执行ID,而非等待结果(见201)。references/advanced.md - 移交到另一个流程——使用启动繁重工作(见
fcode.processes.run("process-identifier", options)/fcode-javascript),并快速返回确认信息(fcode-python/message),而非等待其完成。redirect
仅当请求确实快速,或数据必须在步骤间流动时,才保持同步。对于传递数据,不要阻塞提交——而是:
- ****在表单渲染前在服务器端计算
preRenderProcess(见variables)。references/advanced.md - 多步骤表单通过+
nextProcessId向前传递状态(见下文)。variables
Multi-step forms
多步骤表单
Each step is its own process. Return the next process's slug to advance:
js
return { nextProcessId: "collect-shipping-address" };The field name is still and it accepts a slug or an id — use the
slug, so the same chain works in every workspace.
nextProcessIdThe SDK then renders the form for . Each later step receives all
previous steps' data and results in under a
array. Return a node alongside to pass state forward.
nextProcessIdfcode.context.parametersstepsvariablesnextProcessId每个步骤都是独立的流程。返回下一个流程的slug以推进:
js
return { nextProcessId: "collect-shipping-address" };字段名称仍为,它接受slug或ID——请使用slug,这样同一流程链在每个工作区都能正常工作。
nextProcessIdSDK随后会渲染对应的表单。后续每个步骤都会在的数组中接收所有先前步骤的数据和结果。在旁边返回节点以向前传递状态。
nextProcessIdfcode.context.parametersstepsnextProcessIdvariablesAutomatic file uploads
自动文件上传
A file field is with a key
inside the property:
"type": "string""ui": { "ui:widget": "file" }json
{
"properties": {
"inputFile": { "type": "string", "ui": { "ui:widget": "file" } }
}
}(A root-level map keyed by field name — rjsf's convention — is
also merged, but per-property is the documented form; the root is
mainly for form-level options like . For secret inputs
prefer on the property — it renders a password widget
automatically; see .)
uiuiSchemauiuiui:submitButtonOptions"isSensitive": truefcode-json-schemaOn submit the file is uploaded to Storage before the process starts, and the
parameter arrives as an reference (an array if multiple
files allowed). Strip the prefix to download:
fcode.storage://…js
const { context: { parameters } } = fcode;
const stream = await fcode.storage.download(
parameters.inputFile.replace("fcode.storage://", "")
);Uploaded files count toward storage limits — delete them at the end of the
process if only needed transiently.
文件字段是,并在属性内部包含键:
"type": "string""ui": { "ui:widget": "file" }json
{
"properties": {
"inputFile": { "type": "string", "ui": { "ui:widget": "file" } }
}
}(根级别的映射按字段名称键控——rjsf的约定——也会被合并,但每个属性的是文档化的形式;根级主要用于表单级选项,如。对于机密输入,优先在属性上设置——它会自动渲染密码小部件;见。)
uiuiSchemauiuiui:submitButtonOptions"isSensitive": truefcode-json-schema提交时,文件会在流程启动前上传到存储,参数会以引用的形式传递(如果允许多个文件,则为数组)。去掉前缀即可下载:
fcode.storage://…js
const { context: { parameters } } = fcode;
const stream = await fcode.storage.download(
parameters.inputFile.replace("fcode.storage://", "")
);上传的文件会占用存储配额——如果仅临时需要,请在流程结束时删除它们。
Advanced
进阶内容
For styling and the two themes (including the f0 theme to use when embedding in a
React app inside Factorial), initial/hidden values, async submission, custom
headers, API-host override, variables replacement, pre-rendering current values
into install/settings forms, reacting to user input from your own page (the
React prop, the DOM events), and modal rendering,
read .
onChangefcode-forms-*references/advanced.md关于样式和两种主题(包括在Factorial内部的React应用中嵌入时使用的f0主题)、初始/隐藏值、异步提交、自定义头、API主机覆盖、变量替换、将当前值预渲染到安装/设置表单、从您自己的页面响应用户输入(React的属性、 DOM事件)以及模态渲染,请阅读。
onChangefcode-forms-*references/advanced.md