十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NocoBase RunJS ctx.collectionField:字段元数据、关联配置与渲染分支实战指南

NocoBase RunJS ctx.collectionField:字段元数据、关联配置与渲染分支实战指南 NocoBase RunJS ctx.collectionField字段元数据、关联配置与渲染分支实战指南【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase本文围绕 NocoBase RunJS 执行上下文中的ctx.collectionField展开它是当前 JS 执行位置所绑定的数据表字段CollectionField实例用于获取字段的元数据、类型、校验规则及关联配置。读完本文你将掌握ctx.collectionField的适用场景与空值边界、全部常用属性与方法的源码级语义以及按字段类型分支渲染、访问关联目标数据表、提取枚举选项等可复制的代码模式。什么是 ctx.collectionFieldctx.collectionField是当前 RunJS 执行上下文关联的数据表字段CollectionField实例用于访问字段的元数据、类型、校验规则及关联信息。仅在字段绑定到数据表Collection定义时存在自定义/虚拟字段可能为null无字段绑定的场景中通常为undefined。collectionField: CollectionField | null | undefined;在前端 flow-engine 中这个实例由 CollectionField 类 定义其核心结构是把字段的原始定义options包装为一组按需计算的 getterexport class CollectionField { options: Recordstring, any; collection: Collection; constructor(options: Recordstring, any) { this.options observable({ ...options }); } // ... 各属性均为基于 options 的 getter }从源码结构看CollectionField的每个公开属性name、type、enum、targetCollection等都不是简单字段而是对options与所属collection的派生计算这意味着你在 RunJS 中读取的始终是当前数据表定义下的最新状态例如国际化标题title会经过翻译函数处理并回退到字段名见 title getter。适用场景场景说明JSField表单字段中根据interface、enum、targetCollection等做联动或校验JSItem子表格项中访问当前列对应字段的元数据JSColumn表格列中按collectionField.interface选择渲染方式或访问targetCollection注意ctx.collectionField仅在字段绑定到数据表Collection定义时可用JSBlock 独立区块、无字段绑定的操作事件等场景中通常为undefined使用前建议做空值判断。这一判断在实际代码中随处可见。例如 flow-engine 的权限占位组件在渲染无权限字段时先取model.context.collectionField?.collection再回退到区块自身的collection字段名也做了?.name || fieldPath兜底见 CollectionFieldModel.tsx——这正是文档建议“访问前使用可选链”的实例依据。常用属性结合源码实现属性类型说明namestring字段名如status、userIdtitlestring字段标题含国际化typestring字段数据类型string、integer、belongsTo等interfacestring字段界面类型input、select、m2o、o2m、m2m等collectionCollection字段所属数据表targetCollectionCollection关联字段的目标数据表仅关联类型有值targetstring目标数据表名称关联字段enumarray枚举选项select、radio 等defaultValueany默认值collectionNamestring所属数据表名称foreignKeystring外键字段名belongsTo 等sourceKeystring关联源键hasMany 等targetKeystring关联目标键fullpathstring完整路径如main.users.status用于 API 或变量引用resourceNamestring资源名如users.statusreadonlyboolean是否只读titleableboolean是否可作为标题展示validationobject校验规则配置uiSchemaobjectUI 配置targetCollectionTitleFieldCollectionField关联目标数据表的标题字段关联字段以下结合 CollectionField 源码 说明几个关键属性的实际取值逻辑帮助理解它们的边界行为fullpath 与 resourceName两条不同的定位路径get fullpath() { return this.collection.dataSource.key . this.collection.name . this.name; } get resourceName() { return ${this.collection.name}.${this.name}; }即fullpath带有数据源前缀main.users.status适用于跨数据源场景下的变量引用与 API 定位resourceName仅含数据表名.字段名users.status用于常规资源命名。在多数据源应用中两者并不等价选择时应明确是否需要限定数据源。readonly字段定义与 UI Schema 的双重判断get readonly() { return this.options.readonly || this.options.uiSchema?.[x-read-pretty] || false; }从源码看readonly不仅取决于字段定义中的readonly选项还会读取uiSchema[x-read-pretty]只读展示标记。因此一个字段即使字段定义未标只读只要 UI Schema 以只读方式渲染ctx.collectionField.readonly也会为true——这解释了为什么在 JS 自定义字段中做“只读则降级为纯文本”的分支判断时该属性是可靠的信号。enum从 uiSchema 读取并做类型修正get enum(): any[] { const options this.options.uiSchema?.enum || []; if (this.type integer) { return options.map((v) { if (typeof v ! object) return v; if (v.value null || v.value undefined) return v; return { ...v, label: translateOptionLabel(this.flowEngine, v.label, { ns: lm-collections }), value: Number(v.value) }; }); } return options.map((v) ({ ...v, label: translateOptionLabel(this.flowEngine, v.label, { ns: lm-collections }) })); }两个要点其一枚举选项的真正来源是uiSchema.enum而非字段options.enum自定义字段里写枚举时应写入 uiSchema其二当字段类型为integer时对象型选项的value会被强制转为Number保证与整型字段值比较时不会出现1 ! 1的陷阱。选项label同样经过翻译处理支持国际化。targetCollection按 target 名称解析 Collectionget targetCollection() { return this.options.target this.collection?.collectionManager.getCollection(this.options.target); } get targetCollectionTitleField() { return this.targetCollection?.titleCollectionField; }targetCollection通过所属数据表的collectionManager按target名称解析未配置target时返回undefined。targetCollectionTitleField则是目标表标题字段titleCollectionField的快捷访问用于关联字段展示时确定“用哪个字段作为显示文本”。titleable兼容两种命名get titleable() { return !!(this.options.titleable ?? this.options.titleUsable); }源码同时兼容titleable与titleUsable两种配置键名取前者存在与否否则回退后者。常用方法结合源码实现方法说明isAssociationField(): boolean是否为关联字段belongsTo、hasMany、hasOne、belongsToMany 等isRelationshipField(): boolean是否为关系型字段含 o2o、m2o、o2m、m2m 等getComponentProps(): object获取字段组件的默认 propsgetFields(): CollectionField[]获取关联目标数据表的字段列表仅关联字段getFilterOperators(): object[]获取该字段支持的筛选操作符如$eq、$ne等isAssociationField 与 isRelationshipField按 type 还是按 interface 判断isAssociationField() { return [belongsToMany, belongsTo, hasMany, hasOne, belongsToArray].includes(this.type); } isRelationshipField(): boolean { const relationshipInterfaces [o2o, oho, obo, m2o, createdBy, updatedBy, o2m, m2m, linkTo, chinaRegion, mbm]; return relationshipInterfaces.includes(this.interface); }两者判断维度不同isAssociationField()基于字段typebelongsToMany、belongsTo、hasMany、hasOne、belongsToArray覆盖数据库层的所有关联类型isRelationshipField()基于字段interfaceo2o、oho、obo、m2o、createdBy、updatedBy、o2m、m2m、linkTo、chinaRegion、mbm覆盖的是界面层被归类为“关系型”的字段包括createdBy、updatedBy这类人员选择类字段。在 RunJS 分支逻辑中若关心“值是不是指向另一条记录”用isAssociationField()更贴切若关心“界面上是否是关联选择器”如 m2o 与输入框的渲染差异用 interface 或isRelationshipField()更合适。getComponentProps组件 props 与校验规则的合成器getComponentProps()见 源码将多项配置合并为组件可用的 props展开uiSchema[x-component-props]剔除fieldNames若enum非空则注入optionsinterface multipleSelect时设置mode: multiple关联类型belongsToMany、hasMany、belongsToArray时multiple为真否则maxCount为 1注入target与目标表的template若存在validation通过jioToJoiSchema把校验配置转成 Joi schema并生成带国际化错误信息的rules校验器。在 JS 自定义字段中你可以通过ctx.collectionField.getComponentProps()拿到与系统字段组件一致的默认 props从而保持渲染行为一致。getFields 与 getFilterOperatorsgetFields(): CollectionField[] { if (!this.options.target) return []; if (!this.targetCollection) { throw new Error(Target collection ${this.options.target} not found for field ${this.name}); } return this.targetCollection.getFields(); } getFilterOperators() { const opts this.getInterfaceOptions(); return opts?.filterable?.operators || []; }getFields()仅对关联字段有意义无target返回空数组target配置了但解析不到目标表时会抛出异常调用前建议先确认targetCollection存在。getFilterOperators()则从字段的 interface 注册信息getInterfaceOptions().filterable.operators读取该字段支持的筛选操作符如$eq、$ne可用于在 JS 动态筛选器中按字段能力生成选项而非硬编码操作符列表。示例以下示例完整继承自关联文档均可直接用于 JSField / JSColumn 等 RunJS 编写场景。根据字段类型做分支渲染if (!ctx.collectionField) return null; const { interface: iface } ctx.collectionField; if ([m2o, o2m, m2m].includes(iface)) { // 关联字段显示关联记录 const target ctx.collectionField.targetCollection; // ... } else if (iface select || iface radioGroup) { const options ctx.collectionField.enum || []; // ... }这是最典型的 JSColumn 用法按interface决定渲染策略。由于enumgetter 已内置整数类型的值修正与 label 翻译此处拿到的选项可直接渲染。判断是否为关联字段并访问目标数据表if (ctx.collectionField?.isAssociationField()) { const targetCol ctx.collectionField.targetCollection; const titleField targetCol?.titleCollectionField?.name; // 按目标数据表结构处理 }注意源码中titleCollectionField属于Collection目标表而非字段自身而字段上提供的是targetCollectionTitleField快捷属性见下文第五个示例两者等价前者写法与文档示例保持一致时可加上?.防止targetCol为undefined。获取枚举选项const options ctx.collectionField?.enum ?? []; const labels options.map((o) (typeof o object ? o.label : o));兼容对象型{label, value}与字符串型两种枚举写法与enumgetter 的实际返回结构可能含原始标量值相匹配。根据只读/只展示模式做条件渲染const { Input } ctx.libs.antd; if (ctx.collectionField?.readonly) { ctx.render(span{ctx.getValue?.() ?? -}/span); } else { ctx.render(Input onChange{(e) ctx.setValue?.(e.target.value)} /); }结合上文readonlygetter 的源码逻辑该分支同时覆盖了“字段定义只读”和“UI Schema 只读展示x-read-pretty”两种情况。获取关联目标数据表的标题字段// 关联字段显示时可用目标数据表的 titleCollectionField 获取标题字段名 const titleField ctx.collectionField?.targetCollectionTitleField; const titleKey titleField?.name ?? title; const assocValue ctx.getValue?.() ?? ctx.record?.[ctx.collectionField?.name]; const label assocValue?.[titleKey];targetCollectionTitleField在 源码中定义为this.targetCollection?.titleCollectionField即目标数据表配置中标题字段title field的 CollectionField 实例非关联字段或目标表缺失时为undefined示例中的?? title兜底保证了健壮性。与 ctx.collection 的关系需求推荐用法当前字段所属数据表ctx.collectionField?.collection或ctx.collection字段元数据名、类型、接口、枚举等ctx.collectionField关联目标数据表ctx.collectionField?.targetCollectionctx.collection通常表示当前区块绑定的数据表ctx.collectionField表示当前字段在数据表中的定义。在子表格、关联字段等场景下两者可能不同例如区块绑定orders表而当前 JSColumn 处理的是orders.items子表的name字段此时ctx.collectionField.collection指向子表targetCollection才指向items。相关属性可参考 ctx.collection 文档。注意事项在JSBlock、JSAction无字段绑定等场景中ctx.collectionField通常为undefined访问前建议使用可选链。自定义 JS 字段若未绑定到数据表字段ctx.collectionField可能为null。targetCollection仅在关联类型字段如 m2o、o2m、m2m下存在enum仅在 select、radioGroup 等有选项的字段下存在且实际来源是uiSchema.enum。getFields()在target配置了但目标表不存在时会抛异常调用前建议先判断targetCollection。相关文档ctx.collection当前上下文关联的数据表ctx.model当前执行上下文所在模型ctx.blockModel承载当前 JS 的父区块ctx.getValue()、ctx.setValue()读写当前字段值【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表