视图模板语法
DSL 即「Domain Specific Language」,中文一般译为「领域特定语言」,在《领域特定语言》这本书中它有了一个定义:
一种为特定领域设计的,具有受限表达性的编程语言
从 016 版本开始,Trantor 提供了 DSL v2,一套标准 XML 的 DSL 语法来描述视图资源。若从旧版升级到 DSL v2,需要注意:
- View 标签需要标记
version="2", 代表使用 DSL v2 进行解析 - 表达式使用 attr=”#{expression}” 格式
DSL 语法描述的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。所有的元素都可以有文本内容和属性(类似 HTML 中)
例如
<!-- 顶层布局容器 --><View title="部门信息" version="2"> <!-- 1 --> <Detail title="所属公司" model="model"> <!-- 2 --> <Fields> <!-- 3 --> <Field name="age" /> <!-- 4-1 --> <Field name="name" /> <!-- 4-2 --> </Fields> </Detail></View>
语法及校验
常规 xml 语法,对 Trantor 来说视图模板 xml 是份解析配置。
DSL v2 支持使用 xsd 进行语法校验。请在 View 节点中配置以下属性,并开启 IDE 的 xml 语法校验支持(一般默认开启)。
- 添加 xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”,指定文档规范
- 添加 xsi:noNamespaceSchemaLocation=“https://terminus-trantor.oss-cn-hangzhou.aliyuncs.com/xsi/0.17/base.xsd ,(这里也可以是本地文件相对路径)
<?xml version="1.0" encoding="UTF-8"?><View xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://terminus-trantor.oss-cn-hangzhou.aliyuncs.com/xsi/0.17/base.xsd" version="2" title="人员详情"> <Detail model="trantor_doc_Person" key="person" dataCondition="age < 25"> <Fields> <GroupField title="#{this.data ? this.data.name : '基本信息'}"> <Field name="name"/> </GroupField> <GroupField title="扩展信息"> <Field name="password"/> <Field name="credentials"/> <Field name="birthday"/> </GroupField> </Fields> <Actions> <Action action="GoBack" label="GoBack" layout="Header"/> </Actions> </Detail></View>术语
<View version="2"> <!-- 描述文档的根元素 --> <Table> <!-- 描述根的第一个子元素 --> <Fields></Fields> <!-- 描述根的第二个子元素 --> </Table> </View> <!-- 定义根元素的结尾 -->-
标签: 标记结构,以
<开头,>结尾,可以写自闭合标签 -
元素: 元素可包含其他元素,元素也可以拥有属性
<!-- staticProp 代表静态属性 一般都是string类型 dynamicProp 我们在value中使用#{}标记动态属性 格式 propKey="#{propVal}" 属性值一般为 number boolean array object 等--><View version="2" staticProp="静态属性" dynamicProp="#{{key: val}}"> <Table></Table> <!-- 代表子xml标签 规则也是闭合标签--></View>- 属性: 标记结构,键值对形式定义,一个属性最多出现一次,一个属性只能有一个值,定义多个同名属性后者覆盖前者
<!-- 生效的是 静态属性2 --><View staticProp="静态属性1" staticProp="静态属性2" dynamicProp="#{() => {key: val}}"> <Table></Table> <!-- 代表子xml标签 规则也是闭合标签--></View>注意事项
- 属性: 属性值必须要用引号包裹,单双引号都可以(建议使用双引号)
- 文档必须包含根元素。该元素是所有其他元素的父元素。一般是 View
特殊语法
标签:标签名必须首字母大写
属性:可以只写一个属性名,同 jsx 语法表示值为 true
<Table advanced /><!-- 等价写法: <Table advanced="#{true}"/> -->标签
可定义的标签有不同类型,分为以下几种
-
根标签
- View 根节点标签,最外层根节点标签必须为此标签
-
数据容器标签
-
布局容器标签
-
配置标签(数据容器专有)
约束
不同类型的标签定义的元素会有一些限制
主要为元素本身可定义的位置和其能定义的子元素
根元素
根元素限制为 View 标签,且必须定义
- 定义位置: 全局唯一,xml 最外层,没有父元素,不能定义到其他任意元素下
- 子元素: 只能定义数据容器和布局容器
<View title="部门信息" version="2"> <!-- 根标签 唯一 --> <LinkTabs> <!-- 布局容器 --> <LinkTab title="公司1" /> <LinkTab title="公司2" /> <LinkTab title="公司3" /> </LinkTabs>
<!-- 数据容器 --> <Detail title="所属公司" model="trantor_doc_Company" key="company"> <Fields> <Field name="id" /> <Field name="name" /> </Fields> </Detail>
<!-- 数据容器 --> <Table title="部门人员" model="trantor_doc_Person" condition="department.id = company.id"> <Fields> <Field name="name" /> <Field name="age" /> </Fields> </Table>
<!-- ...根据页面需要,可继续放置容器 --></View>定义元素
在部分容器/控件的属性中,我们可能需要它执行某一个 Action,但属性值很难完整描述具体 Action 的内容,这时候我们可以对它进行扩展。
可直接在容器/控件的 Actions/RecordActions 里定义对应的扩展 Action。添加 key 指定唯一标示,使用时只需要绑定 key 值即可。
视图在解析时会根据属性的操作级别,在下级节点的【Actions/RecordActions】中寻找对应 key 值的 Action 标签。
<View version="2" title="Point Rule" type="List" forModel="customer_TransRuleBO"> <!-- onAdd 为容器内的全局触发的Action, 绑定Actions中定义的 categoryListAdd --> <!-- onSelect 选中行数据触发Action,绑定RecordActions中定义的 categoryListView --> <CascadeList model="md_CategoryBO" onAdd="categoryListAdd" onSelect="categoryListView" > <Actions> <!-- 此处 this.record 获取的是整个 CascadeList 的数据内容 --> <Action key="categoryListAdd" type="Create" targetView="b2c_item_FrontCategoryBO_FrontCategoryDetail" env="#{data: this.record}" openViewType="Dialog"/> </Actions> <RecordActions> <!-- 此处 this.record 获取的是当前节点数据内容 --> <Action key="categoryListView" targetView="app_mro_item_CategoryChannelInfoTO_ProductPoolList"/> </RecordActions> </CascadeList> </View>数据容器
数据容器能使用内置的数据容器(见:数据容器 API)或自定义数据容器去定义
- 定义位置: 定义在根元素或布局容器下,数据容器不能嵌套数据容器
- 子元素: 只能定义配置标签类型的元素
例如:
<View version="2"> <!-- 数据容器Table --> <Table model="customer_TransBO" dataFunction="customer_PagingTransFunc"> <!-- table的第一个子元素 搜索元素 属于定义配置标签类型的元素 --> <Search> <Fields> <Field name="customer.id"/> <Field name="createdAt"/> </Fields> </Search> <!-- table的第二个子元素 展示控件元素 属于定义配置标签类型的元素 --> <Fields> <Field name="customer.id"/> <Field name="accountType"/> <Field name="transRuleOperateTypeDict" label="Account Behaviour"/> </Fields>
<!-- Error TableForm 属于数据容器 --> <!-- <TableForm> ... </TableForm> --> </Table> </View>布局容器
布局容器只能使用内置的布局容器(见:布局容器 API)去定义
- 定义位置: 定义在根元素或布局容器下,布局容器可以嵌套布局容器
- 子元素: 只能定义数据容器或布局容器
例如:
<View version="2"> <!-- 静态容器 --> <LinkTabs> <LinkTab title='Spot' /> <LinkTab title='Plan' /> </LinkTabs></View>配置元素
数据容器专属元素,作为数据容器的配置(见:数据容器 API)存在
- 定义位置: 只能定义在数据容器下
- 子元素: 只能定义配置元素
比如 Search 元素 Fields 元素 RecordActions 元素 Actions 元素
<View version="2" title="accept order"> <Table key="rule" model="settings_RuleBO"> <!-- Table数据容器 --> <Search> <!-- Table的定义配置容器 搜索框内容 --> <Fields> <Field name="ruleDesc" /> <!-- 搜索项目--> <Field name="entityBO" /> <!-- 搜索项目--> <Field name="ruleStatus" /> <!-- 搜索项目--> </Fields> </Search> <Fields> <!-- Table的定义配置容器 table展示行内容 --> <Field name="priority"/> <!-- 行属性内容 --> <Field name="ruleCode"/> <!-- 行属性内容 --> </Fields> <RecordActions label="operation"> <!-- Table的定义配置容器 table展示行操作 --> <Action label="Detail" targetView="settings_RuleBO_AcceptRuleDetail"/> <!-- 行操作项 --> <Action label="edit" targetView="settings_RuleBO_AcceptRuleEdit"/> <!-- 行操作项 --> </RecordActions> <Actions> <!-- Table的定义配置容器 table的全局性操作--> <Action label="新建"/> <!-- table的全局性操作项 --> </Actions> </Table></View>表达式
xml 中的表达式为 key=”#{expression}”
表达式里可以使用controller 里定义的方法和属性以及数据容器设置的上下文
注意:
- 只允许整个表达式包#{},不支持拼接,例如 key=“name:#{this.record.name}”
- 对于数字和布尔的属性,表达式会被自动转换,可以不包#{},例如:
判断是否属于自动转换的属性,需要以语法提示为准,对于自定义组件的属性自动转换暂时还不支持 - 不再支持 true 的简写方式,例如
<Action multi />必须显式标识<Action multi="#{true}" />。若接口语法提示该属性是 boolean 值,也可以简写成<Action multi="true" /> - 由于标准 xml 语法约束,若表达式中含有特殊符号,需要转义。
/*** @desc* name: 静态属性 value 值为 user* label: 以'#{}'包裹 代表动态属性 value 值为 ${label}* 以 #{} 包裹, 描述的 key 值 代表方法 比如* initValue 代表方法 initValue* onChange 代表方法 onChange*/<Field name="user" label="#{label}" initValue="#{'abc'}" onChange="onChange" />
<Field name="user" show="#{this.record.id && this.record.isMale}" />语法
js 语法,表达式里可以使用 loadsh 库来处理数据,使用别名”_“访问
可参考:lodash
/*** name: 静态属性 value 值为 user* _.get() 引用lodash库处理数据 其返回值为获取this.record中的name属性值* label: 以 #{} 包裹, 代表动态属性, 即 value 等于 ${_.get(this.record, 'name')}*/<Field name="user" label="#{_.get(this.record, 'name')}" />符号转义
数据容器上下文
数据容器上下文目前只有数据容器请求模型接口获取的数据信息
使用 this 访问,如果当前数据容器只有一条数据信息,this.data 等于 this.record(建议单条数据都使用 this.record 访问),否则 this.data 为多条数据组成的数组
- this.data: 获取所有数据,值为数组或对象
- this.record: 获取单条数据,值为对象
/*** name 静态属性 value = user* show 是否显示 动态属性 value = ${!this.record.id}* lable label值 动态属性 value = ${this.record.name}*/<Field name="user" show="#{!this.record.id}" label="#{this.record.name}" />注意事项
- 属性不论是否为表达式,值都必须用引号包裹
- 注意表达式里有声明的字符串要用引号包裹,单双引号没有限制,但要注意相同引号嵌套是否需要转义
- 方法可以只写函数名,也可以写表达式,当为表达式时会为其包一层函数并返回表达式的值
this 作用域
表达式的作用域类似作用域链,元素属性表达式中访问的变量会一级一级层层向上查找访问,直到找到或报错
当前层级元素的属性表达式里一定访问不到自身的上下文,只能访问到上级元素中的上下文
示例
<View version="2" title="#{this.record.name}" subtitle="模型名" > <!-- 根标签 view--> <Table key="#{id}" model="user2_Address" type="#{env.type}"> <!-- 数据容器 Table--> <Search> <!-- 数据容器配置标签 Search--> <Fields> <!-- Fields 标签 其子标签必须为 Field配置标签--> <Field name="userList"> <!-- Field 渲染项标签 --> <RenderType> <!-- Field 用来渲染Field的选择标签 --> <Widget customProps="xxx"/> > <!-- Field 选择渲染标签 --> </RenderType> </Field> <Field name="province"/> <!-- Field 单项标签 --> <Field name="city"/> <!-- Field 单项标签 --> </Fields> </Search> <Fields> <Field name="userList"/> <Field name="province"/> <Field name="city"/> <Field name="relTodo"/> </Fields> <RecordActions label="操作"> <!-- table 行配置标签 RecordActions 子标签为单行操作 --> <Action label="删除" logicFunction="user2_Address_delete" after="Refresh" show="#{!this.record.id}"/> <!-- Action 单行操作项 --> <Action label="编辑" targetView="user2_Address_edit"/> <!-- Action 单行操作项 --> <Action label="#{this.record.city}" targetView="user2_Address_detail"/> <!-- Action 单行操作项 --> </RecordActions> <Actions> <!-- table 全局操作项 子标签为Action操作项目 --> <Action type="Create" logicFunction="user2_AddAddress" layout="Footer" /> <!-- Action 全局操作项 --> </Actions> </Table></View>