跳转到内容

视图模板语法

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>

xmlTree.png

语法及校验

常规 xml 语法,对 Trantor 来说视图模板 xml 是份解析配置。

DSL v2 支持使用 xsd 进行语法校验。请在 View 节点中配置以下属性,并开启 IDE 的 xml 语法校验支持(一般默认开启)。

<?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 &lt; 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>

注意事项

  1. 属性: 属性值必须要用引号包裹,单双引号都可以(建议使用双引号)
  2. 文档必须包含根元素。该元素是所有其他元素的父元素。一般是 View

特殊语法

标签:标签名必须首字母大写
属性:可以只写一个属性名,同 jsx 语法表示值为 true

<Table advanced />
<!-- 等价写法: <Table advanced="#{true}"/> -->

标签

可定义的标签有不同类型,分为以下几种

约束

不同类型的标签定义的元素会有一些限制
主要为元素本身可定义的位置和其能定义的子元素

根元素

根元素限制为 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 里定义的方法和属性以及数据容器设置的上下文

注意:

  1. 只允许整个表达式包#{},不支持拼接,例如 key=“name:#{this.record.name}”
  2. 对于数字和布尔的属性,表达式会被自动转换,可以不包#{},例如: 判断是否属于自动转换的属性,需要以语法提示为准,对于自定义组件的属性自动转换暂时还不支持
  3. 不再支持 true 的简写方式,例如 <Action multi /> 必须显式标识 <Action multi="#{true}" />。若接口语法提示该属性是 boolean 值,也可以简写成 <Action multi="true" />
  4. 由于标准 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 &amp;&amp; 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>