标签属性默认值 TagDefault

适用模块:anyline-office(docx / xlsx 模板渲染) · 类:org.anyline.office.tag.TagDefault

1. 它解决什么问题

模板里的标签属性(如 <aol:for items="..." var="...">)通常要一个一个写在模板上。 当一批模板遵循同一套约定时(数据源都叫 items、循环变量都叫 item、无数据都不删行……), 可以用 TagDefault 在 代码里统一登记默认值,模板上就不必再写这些属性。

模板可以从这样:

<aol:for items="${items}" var="item" status="status" remove="false">${item.NAME}</aol:for>

简化成这样(属性全部由默认值提供):

<aol:for>${item.NAME}</aol:for>
只对"没写的属性"生效 标签上显式写了的属性永远优先,默认值不会覆盖它。

2. 优先级

  1. 标签上显式书写的属性(最高,写了就一定用它)
  2. 文件级默认值:TagDefault.set(book.context(), "for.items", ...),只对当前文档生效
  3. 全局级默认值:TagDefault.set("for.items", ...),对所有文档生效
  4. 内置默认值:代码中 BUILTIN 预置的几条(见第 6 节,优先级最低)
  5. 标签自身的缺省值:如 begin 缺省 0、step 缺省 1(兜底)

文件级之所以只对当前文件生效,是因为它借 Context.variables 传递:每个 WDocument/XWorkBook 各持有一个 Context,标签运行的子上下文由 clone() 继承,文件之间互不影响。

3. 三种设置方式

3.1 全局(对所有文档生效)

// 单条 TagDefault.set("for.items", "${items}");
TagDefault.set("for.remove", "false"); // 批量 Map<String,Object> defs = new HashMap<>();
defs.put("for.var", "item");
defs.put("for.status", "status");
TagDefault.set(defs);

3.2 文件级(只对当前文档生效,推荐)

XWorkBook book = new XWorkBook(template); // 注意必须在解析之前设置(save/open 之前) TagDefault.set(book.context(), "for.items", "${rows}");
TagDefault.set(book.context(), "for.remove", "false");
book.save(out);

3.3 直接给对象(不经过 EL 解析)

// 数据源可以直接给集合对象,不必先塞进上下文 TagDefault.set(book.context(), "for.items", dataSet);

4. key 的写法

  • 格式:标签名.属性名,大小写不敏感(内部统一小写),如 for.items、chart.type。
  • 别名自动匹配:取值时按代码里传入的别名顺序依次查找。例如 data, d, items, is 会依次查 for.data → for.d → for.items → for.is,命中即返回。
  • 通配:* 表示对所有标签生效,如 *.items;精确 key 优先于通配 key。
只登记主属性名 因为别名是按顺序抢先命中的,如果同时登记了 for.data 和 for.items, for.data 会抢在前面命中,你后来设的 for.items 就失效了。 一个属性只登记一个 key(主属性名)。

5. value 的三种形态

形态 写法 取值时的行为
EL 表达式 "${items}" 按当前上下文解析成对象;解析不出来就是 null
普通字符串 "item" / "false" 原样返回(布尔/数字属性会再按类型解析)
对象 dataSet / list 直接返回这个对象,常用作数据源

null 存不进去——传 null 等同于删除这条默认值。

6. 内置默认值

key 值 含义
for.items ${items} 不写数据源时,默认取上下文中的 items 变量
for.var item 不写 var 时,迭代变量名是 item
for.status status 不写 status 时,状态变量名是 status(${status.index})

它们是优先级最低的一档,可用文件级或全局级覆盖;想让状态变量回到老的 s:

TagDefault.set("for.status", "s"); // 全局
status 默认值变更提醒 内置 for.status 为 status。历史模板里"没写 status 却使用 ${s.index}"的写法会失效, 请在模板上显式写 status="s",或按上面一行代码把默认值改回 s。

7. 哪些属性可以设默认值

docx:标签的取属性统一走 AbstractTag.fetchAttribute*(),所以所有标签的所有属性都支持默认值。

xlsx:XRow 中 aol:for 的下列属性走默认值(data 系列单独按对象取值,见第 9 节):

属性 别名 key 说明
items data d is for.items 数据源(内置 ${items})
var — for.var 迭代变量名(内置 item)
status — for.status 状态变量名(内置 status)
remove — for.remove 无数据时是否删除模板行,"false" 表示保留
begin start b for.begin 起始下标
end e for.end 结束下标
step — for.step 步长

8. 完整示例

8.1 xlsx:一套约定跑所有模板

// 全局约定:数据源统一叫 items,无数据保留模板行 TagDefault.set("for.remove", "false");

XWorkBook book = new XWorkBook(template);
book.variable("items", dataSet); // 内置 for.items=${items} 会取到它 TagDefault.set(book.context(), "for.end", "99"); // 该文件最多渲染100行 book.save(out);

模板只需写:

<aol:for>${item.NAME}</aol:for>

8.2 同一模板渲染多次、每次不同约定

XWorkBook b1 = new XWorkBook(template);
TagDefault.set(b1.context(), "for.items", "${listA}"); // 只影响 b1 b1.save(out1);

XWorkBook b2 = new XWorkBook(template);
TagDefault.set(b2.context(), "for.items", "${listB}"); // 只影响 b2 b2.save(out2);

8.3 作用域 / 遍历范围(docx)

TagDefault.set(book.context(), "for.scope", "tr"); // 默认按表格行循环

9. docx 与 xlsx 的差异

项 docx xlsx
覆盖属性范围 全部标签的全部属性 aol:for 的属性(见第 7 节)
数据源取值 data() → fetchAttributeData() 单独调用 TagDefault.fetch(),返回值可以是对象(DataSet/List),不走字符串转换
内置默认值 同一份 BUILTIN,两边一致

10. 删除 / 清空 / 查询

TagDefault.remove("for.items"); // 删一条全局默认值 TagDefault.remove(book.context(), "for.items"); // 删当前文件的一条 TagDefault.clear(); // 清空全局(不影响内置) TagDefault.clear(book.context()); // 清空当前文件 TagDefault.get("for.items"); // 查全局(不解析EL) TagDefault.defaults(book.context()); // 当前文件的全部默认值

内置默认值无法删除,只能用文件级/全局级覆盖。

11. 常见坑

  1. 写了属性但取不到值,不会再走默认值。
    判断依据是"属性有没有出现在标签上",不是"有没有取到值"。 items="${list}" 而 list 不存在时,结果是空,不会被默认数据源顶替。
  2. 取不到数据源 ≠ 空集合。
    变量不存在时 aol:for 只打一条 warn 并保留模板行;只有取到空集合时才按 remove 处理(默认删除模板行)。
  3. 文件级必须传该文档的 context。
    要 TagDefault.set(book.context(), ...);传错 context 就配到别的文件上(或完全不生效)。
  4. 设置时机要在解析之前。
    new XWorkBook(file) 之后、save()/open() 之前设置。
  5. 别名别重复登记。
    见第 4 节:for.data 会抢在 for.items 前面命中。
  6. xlsx 的 status 默认已改为 status。
    老模板里 ${s.index} 要么显式写 status="s",要么 TagDefault.set("for.status","s")。