将 Slot 绑定到变量
将一个或多个 Slot 绑定到变量,使其内容与变量保持同步。
配置绑定
安装 @tiptap-pro/extension-slot 和 @tiptap-pro/extension-variable。
首先,为两个 Slot 设置相同名称的 variable 属性:
const content = {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'inlineSlot',
attrs: { id: 'customerField', variable: 'customer' },
content: [{ type: 'text', text: 'Ada' }],
},
{ type: 'text', text: ' / ' },
{
type: 'inlineSlot',
attrs: { id: 'signatureField', variable: 'customer' },
content: [{ type: 'text', text: 'Ada' }],
},
],
},
],
}然后,使用这两个扩展创建编辑器:
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { Slot } from '@tiptap-pro/extension-slot'
import { Variable } from '@tiptap-pro/extension-variable'
const editor = new Editor({
extensions: [StarterKit, Slot, Variable],
content,
})变量值会直接显示在每个 Slot 中,无需额外的变量节点。
由 Slot 提供变量值
未为 customer 设置显式值或解析器时,第一个绑定的 Slot 会提供变量值。你可以编辑任一 Slot,或使用命令填写。变量和另一个 Slot 会一起更新:
editor.commands.fillSlot({
id: 'customerField',
content: [{ type: 'text', text: 'Grace' }],
})填写成功时,命令会返回 true。单纯绑定变量不会使 Slot 变为只读。
如果依赖的解析器失败、新内容无法放入其他绑定的 Slot,或 Content Protection 阻止了同级 Slot 更新,命令会返回 false,并保留之前的文档和变量值。Variable.onError 会收到错误,slotCommandRejected 会报告 code: 'variableSyncFailed'。试运行不会执行解析器,也不会预测同步失败。
由变量控制 Slot
显式值或解析器的优先级高于 Slot 内容。设置变量会重新填写两个 Slot,并使它们变为只读:
editor.commands.setVariable({ name: 'customer', value: 'Lin' })若要改用解析器:
const currentCustomer = { name: 'Lin' }
editor.commands.setVariables({ variables: { customer: () => currentCustomer.name } })变量控制 Slot 时,fillSlot()、fillSlots() 和 clearSlot() 都会返回 false,且不会更改 Slot 内容,即使替换值与当前值相同也是如此。editor.can() 对这些写入操作也会返回 false:
const filled = editor.commands.fillSlot({
id: 'customerField',
content: [{ type: 'text', text: 'Grace' }],
})
// filled === false; both slots still display "Lin".批次或命令链中的写入被拒绝时,其中所有写入都不会生效。通过父级或嵌套 Slot 写入内容也会被 Slot 命令拒绝。要更改受控内容,请更新变量。
若要调试或向应用提供反馈,请监听 Slot 扩展的 slotCommandRejected 事件:
editor.on('slotCommandRejected', ({ command, code, slotIds }) => {
if (code === 'variableControlled') {
console.log(`${command} refused: a variable controls the slot`, slotIds)
}
})此类拒绝通过 slotCommandRejected 报告,而不是 Variable.onError。试运行不会触发事件。Variable 插件拒绝的其他编辑仍会通过 Variable.onError 报告。
移除显式定义后,Slot 会重新变为可编辑,其当前内容会成为变量值:
editor.commands.removeVariables({ names: ['customer'] })显式设置为 null 或 undefined 也会使 Slot 变为只读,此时它们会显示空内容。
更改绑定
将现有 Slot 绑定到变量,或更改当前绑定:
editor.commands.bindSlotVariable({ id: 'customerField', name: 'customer' })未知名称会创建一个由 Slot 驱动的变量。如果该变量已有绑定的 Slot,新绑定的 Slot 会采用其当前值。
移除绑定,同时保留 Slot 内容:
editor.commands.unbindSlotVariable({ id: 'customerField' })富文本内容
纯文本 Slot 会提供字符串。带格式的 Slot 会提供 Tiptap JSON,并保留标记、换行和结构:
editor.commands.setVariable({
name: 'customer',
value: { type: 'text', text: 'Ada', marks: [{ type: 'bold' }] },
})- 内容必须能放入所有绑定的 Slot。多段内容请使用块级 Slot。
- 另一个已绑定 Slot 内部的绑定会被忽略,但仍会保存;移除外层绑定或将内层 Slot 移到外部后,这些绑定会生效。
- 其他对象会显示为 JSON 文本。
null和undefined会清空 Slot。
即使用户添加了格式,如果依赖的解析器需要 Slot 的纯文本,也请使用 getText():
editor.commands.addVariable({
name: 'greeting',
value: ({ getText }) => `Hello, ${getText('customer')}!`,
})编辑 customer Slot 会自动更新问候语。若要保留 Tiptap JSON,请改用 get()。
保存和保护
Slot 内容和绑定会随文档一起保存。绑定支持撤销和重做。运行时值和解析器保留在编辑器配置中。
在协同编辑器中,即使客户端本地变量值不同,收到的 Slot 内容仍会保留。光标移动和无关编辑不会恢复本地变量值。显式更新本地变量可以将其值写入共享 Slot。若要让所有客户端显示相同的变量值,请保持运行时定义一致。
使用 Content Protection 时:
- 本地编辑需要有权限更新每个绑定的 Slot。
- 无法读取的源 Slot 会隐藏其驱动的变量及依赖值,直到恢复访问权限。
- 收到的协同编辑仍会应用。本地的额外 Slot 写入需要编辑权限。
同步在客户端编辑器中运行。加载文档时,请重新提供显式值和解析器。