如何使docxtemplater保留无对应数据的循环与条件占位符?
如何使docxtemplater保留无对应数据的循环与条件占位符?
我太懂你的烦恼了——普通占位符靠nullGetter能原样保留,但像<#condition>这种条件判断、或者循环表格行这类块级标签,一没对应数据就直接消失,用户根本没法直观看到漏了哪些字段对吧?
其实问题根源在于:nullGetter只负责处理普通变量占位符(比如{name}),而条件、循环这类指令是由docxtemplater专门的模块(条件模块、循环模块)处理的,默认逻辑是数据不存在就清空对应内容。要解决这个问题,咱们得自定义这些模块的行为,让它们在数据缺失时保留原始标签。
下面给你具体的实现步骤:
1. 自定义条件模块,保留缺失的条件占位符
先继承docxtemplater默认的ConditionModule,重写它的render方法——当条件字段不存在或为假时,不删除内容,而是把原始的条件标签和内部文本保留下来:
const { ConditionModule } = require("docxtemplater"); class CustomConditionModule extends ConditionModule { render(part, options) { const { data } = options; const conditionName = part.value; // 检查数据里是否存在该条件,或条件值是否为真 if (data[conditionName] == null || !data[conditionName]) { // 拼接原始的条件标签和内部内容,确保XML格式合法 const rawConditionContent = `<w:t><#${conditionName}> ${part.innerXml} <#/${conditionName}></w:t>`; return this.createXml(rawConditionContent); } // 条件存在时,沿用默认的渲染逻辑 return super.render(part, options); } }
2. 自定义循环模块,保留缺失的循环行
同理,继承默认的LoopModule,重写render方法——当循环数组不存在或为空时,保留原始的循环标签和表格行内容:
const { LoopModule } = require("docxtemplater"); class CustomLoopModule extends LoopModule { render(part, options) { const { data } = options; const loopName = part.value; const loopData = data[loopName]; // 检查循环数据是否不存在、不是数组或为空数组 if (loopData == null || !Array.isArray(loopData) || loopData.length === 0) { // 保留原始循环标签和内部行内容 const rawLoopContent = `<w:t>{#${loopName}} ${part.innerXml} {/${loopName}}</w:t>`; return this.createXml(rawLoopContent); } // 循环数据存在时,使用默认逻辑渲染 return super.render(part, options); } }
3. 注册自定义模块并整合nullGetter
创建docxtemplater实例时,把自定义的模块注册进去,同时保留你原来的nullGetter处理普通占位符:
const Docxtemplater = require("docxtemplater"); const fs = require("fs"); const path = require("path"); // 读取你的模板文件 const templateContent = fs.readFileSync(path.resolve(__dirname, "你的模板文件.docx"), "binary"); const doc = new Docxtemplater(templateContent, { modules: [ new CustomConditionModule(), new CustomLoopModule(), ], // 保留你原来的nullGetter逻辑 nullGetter: (part) => { if (!part.module) { // 这里替换成你实际使用的分隔符 return `<${part.value}>`; } return ""; }, // 如果你的模板用了自定义分隔符,记得在这里配置 delimiters: { start: "<", end: ">" } }); // 传入你的数据(哪怕缺少某些条件或循环字段) doc.render({ // 示例数据:比如缺少condition和loopList字段 username: "测试用户" }); // 生成处理后的文档 const outputBuffer = doc.getZip().generate({ type: "nodebuffer" }); fs.writeFileSync(path.resolve(__dirname, "处理后文档.docx"), outputBuffer);
一些注意事项
- 如果你模板里的分隔符是
{和},记得把代码里的<#改成{#,<#/改成{/,保持和模板一致。 - 代码里的
part.innerXml是条件/循环块内部的XML内容,直接拼接后要确保XML格式合法,不然生成的docx可能无法打开,测试时可以多调整几次。 - 复杂表格的循环行可能需要更精细的处理,比如保留原始表格的XML结构,而不是简单拼接文本,你可以根据自己的模板结构微调。
备注:内容来源于stack exchange,提问作者Antheriox
相关产品推荐
相关产品推荐

