You are currently offline, serving cached version
10 December 2024 : If you are using angular-expressions to parse expressions such as {user.name}, {#users.length > 10}, please upgrade asap to angular-expressions@1.4.3 for security reasons : View Github issue

Subtemplate module

This module exposes a tag to include a subtemplate (another Word document). The subtemplate is embeded into your document. Only the document content is embedded, footers and headers are not since they are unique for each document, so the module keeps the headers/footers from your base template.

The styles of the resulting generated document will be the same as the styles from the main template, which means that styles that exist in both the template and the included document will use the styles of the main template. Hence, if both your main template and your included document have Heading1, the style that will be applied to all Heading1 will be the style from the main template.

The subtemplate module and the subsection module serve distinct purposes. The subtemplate module facilitates the insertion of another document's content into the current one, specifically focusing on the main content (no headers/footers). In contrast, the subsection module specifically handles the inclusion of headers and footers from an external document while excluding its main content (This is what Word calls a "section"). It's important to note that by utilizing both the subtemplate and subsection modules, one can seamlessly integrate the entirety of an external document, encompassing headers, footers, and main content.

Usage

Your docx should contain the text: {:include subtemplate}.

In your data, the key subtemplate should be another docxtemplater instance.

Usage (nodejs)

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const fs = require("fs");

const headerZip = new PizZip(fs.readFileSync("header.docx"));
const footerZip = new PizZip(fs.readFileSync("footer.docx"));
const headerDoc = new Docxtemplater().loadZip(headerZip);
const footerDoc = new Docxtemplater().loadZip(footerZip);

const zip = new PizZip(fs.readFileSync("template.docx"));
const doc = new Docxtemplater(zip, {
    modules: [new SubtemplateModule({})],
});
doc.render({
    subtemplate: headerDoc,
    footer: footerDoc,
});

Usage (browser)

<html>
    <script src="node_modules/docxtemplater/build/docxtemplater.js"></script>
    <script src="node_modules/pizzip/dist/pizzip.js"></script>
    <script src="node_modules/pizzip/vendor/FileSaver.js"></script>
    <script src="node_modules/pizzip/dist/pizzip-utils.js"></script>
    <script src="build/subtemplate-module.js"></script>
    <script>
        function loadDocument(file) {
            return new Promise(function (resolve, reject) {
                PizZipUtils.getBinaryContent(
                    file,
                    function (error, content) {
                        if (error) {
                            return reject(error);
                        }
                        resolve(content);
                    }
                );
            });
        }

        function parser(tag) {
            return {
                get(scope, context) {
                    const isIncludeTag =
                        context &&
                        context.meta &&
                        context.meta.part &&
                        context.meta.part.module ===
                            "pro-xml-templating/include";

                    const value = scope[tag];
                    if (isIncludeTag && value) {
                        if (value instanceof Array) {
                            return Promise.all(
                                value.map(function (v) {
                                    return loadDocument(v).then(
                                        function (document) {
                                            const doc =
                                                new docxtemplater(
                                                    new PizZip(
                                                        document
                                                    ),
                                                    {
                                                        paragraphLoop: true,
                                                        parser,
                                                        linebreaks: true,
                                                    }
                                                );
                                            doc.render(scope);
                                            return doc;
                                        }
                                    );
                                })
                            );
                        }
                        return loadDocument(value).then(
                            function (document) {
                                const doc = new docxtemplater(
                                    new PizZip(document),
                                    {
                                        paragraphLoop: true,
                                        parser,
                                        linebreaks: true,
                                    }
                                );
                                doc.render(scope);
                                return doc;
                            }
                        );
                    }
                    return value;
                },
            };
        }

        loadDocument("demo_template.docx")
            .then(function (document) {
                const doc = new docxtemplater(
                    new PizZip(document),
                    {
                        parser,
                        paragraphLoop: true,
                        linebreaks: true,
                        modules: [
                            new DocxtemplaterSubtemplateModule(),
                        ],
                    }
                );
                return doc
                    .renderAsync({
                        header: "examples/demo_header.docx",
                        footer: "examples/demo_footer.docx",
                    })
                    .then(function () {
                        const out = doc.getZip().generate({
                            type: "blob",
                            mimeType:
                                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                        });
                        saveAs(out, "generated.docx");
                    });
            })
            .catch(function (err) {
                console.log("error while loading documents");
                console.log(err);
            });
    </script>
</html>

After installing the module, you can use a working demo by running node sample.js.

Subtemplate Rendering

You can render the values in your subtemplate by using a custom parser and render the document with the current scope.

For example :

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const expressionParser = require("docxtemplater/expressions.js");
const fs = require("fs");

function parser(tagString) {
    const exp = expressionParser(tagString);
    return {
        get(scope, context) {
            const isIncludeTag =
                context &&
                context.meta &&
                context.meta.part &&
                context.meta.part.module ===
                    "pro-xml-templating/include";
            const value = exp.get(scope, context);
            if (isIncludeTag && value) {
                if (value instanceof Array) {
                    return value.map(function (v) {
                        const zip = new PizZip(
                            fs.readFileSync(v)
                        );
                        const doc = new Docxtemplater(zip, {
                            paragraphLoop: true,
                            parser,
                            linebreaks: true,
                        });
                        doc.render(scope);
                        return doc;
                    });
                }
                const zip = new PizZip(fs.readFileSync(value));
                const doc = new Docxtemplater(zip, {
                    paragraphLoop: true,
                    parser,
                    linebreaks: true,
                });
                doc.render(scope);
                return doc;
            }
            return value;
        },
    };
}

const doc = new Docxtemplater(zip, {
    modules: [new SubtemplateModule({})],
    parser,
});

doc.render({
    header: "examples/demo_header.docx",
    footer: "examples/demo_footer.docx",
});

Including multiple documents with a single tag

Note that it is possible to include several documents using the tag : {:include docs}, by simply using an array of documents in the related data attribute.

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const expressionParser = require("docxtemplater/expressions.js");
const fs = require("fs");

function parser(tagString) {
    const exp = expressionParser(tagString);
    return {
        get(scope, context) {
            const isIncludeTag =
                context &&
                context.meta &&
                context.meta.part &&
                context.meta.part.module ===
                    "pro-xml-templating/include";
            const value = exp.get(scope, context);
            if (isIncludeTag && value) {
                const zip = new PizZip(fs.readFileSync(value));
                const doc = new Docxtemplater(zip, {
                    paragraphLoop: true,
                    parser,
                    linebreaks: true,
                });
                doc.render(scope);
            }
            return value;
        },
    };
}

const doc = new Docxtemplater(zip, {
    modules: [new SubtemplateModule({})],
    parser,
});
doc.render({
    docs: ["doc1.docx", "doc2.docx"],
});

keepStyles mode

When importing a document, the imported document will normally inherit styles from the main document.

For example, if the main document defines the style "Heading1" as purple with a font family of Courrier New, all "Heading1" that come from the included document will also be purple with Courrier new.

Since version 3.17.0, there is a new mode called keepStyles. With this mode, all styles defined in the subtemplate will be renamed if necessary so that the documents are added with the style of the included document.

Use this option like this :

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const fs = require("fs");

const headerZip = new PizZip(fs.readFileSync("header.docx"));
const footerZip = new PizZip(fs.readFileSync("footer.docx"));
const headerDoc = new Docxtemplater().loadZip(headerZip);
headerDoc.keepStyles = true;
const footerDoc = new Docxtemplater().loadZip(footerZip);
footerDoc.keepStyles = true;

const zip = new PizZip(fs.readFileSync("template.docx"));
const doc = new Docxtemplater(zip, {
    modules: [new SubtemplateModule({})],
});
doc.render({
    subtemplate: headerDoc,
    footer: footerDoc,
});

Subsections

It is possible to include subsections, however, if you only have access to the subtemplate module, the headers will not be included.

If you have access to the subsection module, you can do the following to also include the headers and footers of the included document :

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const SubSectionModule = require("docxtemplater-subsection-module");
const doc = new Docxtemplater(zip, {
    modules: [
        new SubtemplateModule({
            subsection: new SubSectionModule(),
        }),
    ],
});
doc.render(data);

When using the SubSectionModule with the SubtemplateModule, by default, the section where your tag {:include doc} is kept, meaning that it will create a pagebreak before adding the document, and also at the end of the included document, it will create a pagebreak.

If you would prefer to not have this pagebreak, than the currentsubsection of the document is going to be replaced by the one from the subtemplate. To do this, you have two attributes that you can set on the included document, replaceFirstSection and replaceLastSection :

const fs = require("fs");
const includedContent = fs.readFileSync("included.docx");
const includedDoc = new Docxtemplater(
    new PizZip(includedContent)
);
includedDoc.replaceFirstSection = true;
includedDoc.replaceLastSection = true;
doc.render({ included: includedDoc });

Segments

The SegmentModule is part of the same package, and allows you to define a "segment" in the document that can then be included multiple times in the document.

You can use it like this in your template :

{:segment test1}
This is the segment 1
It can contain all {tags}.
{:segment/}

{#users}
{:includesegment test1}
{/}

In your code, write like this :

const SegmentModule =
    require("docxtemplater-subtemplate-module").Segment;
const doc = new Docxtemplater(zip, {
    modules: [new SegmentModule()],
});
doc.render(data);

Dynamic segment

When you use segments, you can use the {:includesegment* segment.value} to include a segment dynamically.

For example, with following data :

doc.render({
    parts: ["part2", "part2", "part1"],
});

You can first define your segments in your code, and then use the {includesegment* .} to include the segments in the order you want them to be.

Your docx will look like this :

{:segment part1}
My first part
{:segment/}
{:segment part2}
My other part
{:segment/}
{#parts}
{:includesegment* .}
{/}

This will use the value from the array parts to include the segment part2 (twice), and then the segment part1.

CHANGELOG

3.19.0

Bugfix when using together with meta module, the following would not work correctly :

(This also needs the meta module version 3.12.0 or higher).

const DocxtemplaterMetaModule = require("docxtemplater-meta-module");
const SubtemplateModule = require("docxtemplater-subtemplate-module");
const metaModule = new DocxtemplaterMetaModule();
const doc = new Docxtemplater(zip, {
    modules: [metaModule, new SubtemplateModule()],
});
metaModule.restrictEdition({
    password: "test123",
    salt: "mHQ9f0jyV8snSWoFHQwZigBo",
});
doc.render(/* data */);

This now works correctly with the subtemplate module.

It also needs the latest version of docxtemplater, 3.51.0.

3.18.0

Bugfix when using keepStyles option, the docDefaults from the included document are now taken into account.

Correctly copy over colors for colors defined in the style.xml as themeColors.

3.17.5

Bugfix when using following template :

{:replacesection doc1}
XXX
{:include doc2}

Previously, the headers / footers would be overwritten by the "doc2" element.

Now, the headers and footers are correctly replaced in this case.

3.17.4

Make it possible to use prefix from the constructor

3.17.3

Add typescript typings to be able to change the module prefix

3.17.2

Please upgrade both to the latest version of docxtemplater and also to the latest version of the subsection module when you upgrade to this version.

Currently, those are : docxtemplater@3.47.2 and docxtemplater-subsection-module@3.5.8

When used together with the subsection module, the following behavior was happening in previous versions :

When using the Subtemplate and Subsection mode together, if your master document defined multiple headers, one for the first page and one for the other pages, and you include a subdocument somewhere in your document, the section after the included document would now use the first page header. Instead, it now uses the default header.

3.17.1

Many improvements to the keepStyles, it fixes multiple bugs for :

  • default styles (normal styles in the included template)
  • numbering which would previously not always use the included template style

3.17.0

Add support for keepStyles option, which will is a mode where the document included will not inherit the styles from the main document.

3.16.16

When updating to this version, please also update your docxtemplater library to at least docxtemplater@3.46.2

This new version fixes a rare bug when including a document which itself is including another document containing a chart, and which resulted in a corrupt document.

3.16.15

Bugfix when working with the subsection module.

In previous versions, when using the SubtemplateModule with a wrapped SubsectionModule, like this :

const SubtemplateModule = require("docxtemplater-subtemplate-module");
const SubSectionModule = require("docxtemplater-subsection-module");
const doc = new Docxtemplater(zip, {
    modules: [
        new SubtemplateModule({
            subsection: new SubSectionModule(),
        }),
    ],
});
doc.render(data);

Right after the {:include doc} tag, the footer and header used after the included doc is present is the same header/footer as the one from the included doc. However, it should after the {:include doc} tag, use the header/footer from the main document that was specified before the {:include} tag.

3.16.14

Upgrade module to use NodeNext moduleResolution setting. See explanation here

3.16.13

Bugfix when an included document contains a "Microsoft visio" file.

In previous versions, the output document would be corrupted, now it works well.

3.16.12

Use new resolvedId API => requires docxtemplater@3.43.0

3.16.11

Bugfix when including :

  • A document that has charts that are created by the chart module.
  • A document with a sunburst chart (Extended Chart type).

Now, the document will be generated correctly (without any corruption).

3.16.10

Throw specific error if trying to include a subtemplate that is of "pptx" fileType

Previously the stacktrace shown was :

TypeError: Cannot read properties of undefined (reading '0')
at SubtemplateModule.addDocument (/home/edgar/www/xtm/modules/subtemplate/es6/index.js:624:65)

3.16.9

When trying to include a document that has no styles.xml or no settings.xml file, the module would throw an error.

Now, the document is included correctly.

3.16.8

Allow to insert emf images (use correct content-type of image/x-emf)

Bugfix when included contains Target=NULL

3.16.7

Fix bug when using renderAsync and parser compilation fails : the error should now be shown inside a multierror. To to this, we know use sm.getValueAsync in resolve instead of sm.getValue

3.16.6

Bugfix to set or get xml attributes correctly, even when using tabs instead of spaces in tags attributes

This fixes a corruption when including a document that has tabs instead of spaces in its attributes.

3.16.5

Set module.priority in order to have no more issues related to module ordering

3.16.4

Bugfix when used together with HTML module, when the document contains a comment + a {~~html} tag

The following error was thrown :

TypeError: Cannot read properties of undefined (reading 'parts')
at HtmlModule.render (xtm/modules/html/js/index.js)

Now this error is fixed.

3.16.3

Bugfix, when using async mode and having empty data, following error was thrown :

Cannot read properties of null (reading 'length')

Now, no more error is thrown.

3.16.2

In some cases, when including a file that contained a chart, following stacktrace would appear :

Uncaught (in promise) Cannot read properties of undefined (reading 'asBinary')

This error is now fixed, the template is correctly generated.

3.16.1

Allow to include files that have images in <v:imagedata> tags.

Previously, the image would not be imported correctly.

3.16.0

Improve support when having continuous sectionbreaks.

In previous versions, continuous sectionbreaks would sometimes be replaced by page section breaks, which completely broke some templates.

3.15.3

Upgrade to correctly pass in fileType, contentType, … to postparse, subRender and subResolve

Makes it compatible with docxtemplater 3.40.1

3.15.2

Bugfix for segment module when inside section changes.

Previously, the following error was thrown :

this2.getNextWSect is not a function

Now the generation works well.

3.15.1

Add module.clone() internal to make module work well with subsegment module

3.15.0

Correctly render tags that are present in comments. (Needs docxtemplater@3.39.0)

3.14.3

Small bugfix when using Segment module with Image and HTML module in async mode.

3.14.2

Improve support for renderAsync when using together with Segment module.

3.14.1

Enhance Segment module to use the correct scope, and make segment module work well even when it contains loops.

3.14.0

Add support for {:subsegment* segment.value} tag

3.13.0

Add support to use the segment module inside the subtemplate module.

This allows you to define a segment in a subtemplate, and then use the segment from the main template.

3.12.4

Bugfix segment module error message :

The subtemplate module handles only docx filetype, not pptx

This now won't show up (the segment module should be ignored in pptx).

3.12.3

Bugfix segment module.

This fixes a bug of the Segmentmodule (part of the subtemplate module) where the segment would show wrongly an error of "Unclosed loop" when using the segment module with following template :

{:segment s}
{#loop}{#loop}{value} {/}{/}
{:segment/}
{#loop}
{:includesegment s}
{/}

This version needs version 3.37.7 of docxtemplater core.

3.12.2

Bugfix on numbering items when including a subtemplate.

Previously, the color and style of the bullets would sometimes use the style from the included template.

Now the style of the bullets (if the same paragraphstyle is used), should use the style of the master document.

3.12.1

Bugfix when using the segment module and the subsegment contains a raw tag, such as this :

{:segment abc}
{@x = y + z }
{x}
{:segment/}

Now, this will work without any error.

3.12.0

Avoid adding styles with w:default="1" set.

Correctly handle insertion of external styles.

Avoid changing the "headerFromTop" property of the page when including a document.

3.11.10

Bugfix corruption when footnotes are included and some footnotes are referenced from a table, and some footnotes are referenced from a paragraph.

3.11.9

Bugfix corruption when footnotes are in the main document and in the subtemplate.

3.11.8

Bugfix to work well when having footnotes or endnotes containing images or links.

3.11.7

Reuse the same extension when including files containing images :

If including a "jpeg" file, it will also copy that file as "jpeg", if it is "png" it will copy as "png", …

3.11.6

Add support for including subtemplates that contain SVG images

3.11.5

Add async support for segment module

Fix Github Issue #669

3.11.4

Update typing file to allow subsection inclusion.

3.11.3

Update to work well with docxtemplater 3.31.0

3.11.2

Avoid error when used together with subsection module

3.11.1

Make module work well in the browser without using xmldom (using window.XMLSerializer)

3.11.0

Automatically convert colors that use "accent1", "accent2", …

This means that if your main document and included document have different themeColors, the color used by the output document will be the ones from the main document.

3.10.2

Bugfix to be able to include 3 subtemplates that each contain a chart.

3.10.1

Bugfix when including multiple subtemplates that each have charts.

3.10.0

Add support to include document that contains footnotes or endnotes.

3.9.2

When using the {:include doc} tag from a header or a footer, the section breaks inside of the included documents are dropped.

This was causing a corruption in previous versions.

3.9.1

Correctly change all "basedOn", "next", and "link" styles when including a document.

3.9.0

Make module compatible with docxtemplater@3.28.0. Please make sure to update docxtemplater to 3.28.0 at the same time you update this module. The internal change made is the use of the new matchers API which fixes bugs that were triggered depending on the order of the modules that are attached to the instance. Now the order of the modules should not matter as expected.

3.8.0

Make module compatible with docxtemplater@3.27.0. Please make sure to update docxtemplater to 3.27.0 at the same time you update this module

3.7.1

Bugfix to have the subtemplate work with Office 365 files (files which use word/document2.xml instead of word/document.xml).

The module works in all possible cases now :

  • when the including file is an Office 365 file
  • when the included file is an Office 365 file
  • when using an Office 365 file with the self-including module

3.7.0

Add Subtemplate.Segment module, to allow to define a segment in a document and include it (in the same document).

3.6.7

Use @xmldom/xmldom instead of xmldom, see this github issue

3.6.6

When including a document that has subsections, the header / footer of the main document should be preserved in all cases.

3.6.5

Generate files in built with correct filename In previous versions, the filename was always build/docxtemplater.js. Now the filename is build/subtemplate-module.js The .min.js file is also created now.

3.6.4

Add typescript definitions for public API

3.6.3

Move webpack from dependency to devDependency (which triggered an error during installation on node version 10)

3.6.2

Internal change to allow to match tags with non-breaking space.

When entering Ctrl+Shift+Space, a "non-breaking" space is created.

When using a non-breaking space in the tag {:subtemplate doc}, the tag would not be replaced correctly.

3.6.1

Calculate maxDocPrId lazily (only at the time when we need the value), to avoid overhead when attaching the module.

3.6.0

To better match with the general behavior of docxtemplater, if some subtemplate is null or undefined, nothing will be rendered instead of getting the error : "The subtemplate for the tag 'foobar' does not exist".

This is now the same as with the loop module and the image module for example.

If you give a value that is not null or undefined but is not a valid Docxtemplater document, an error will still be shown.

3.5.6

Continuation of 3.5.5. There was still one case that would throw Cannot read property 'asArrayBuffer' of undefined when using inline images (ones present within a paragraph, inside the content).

The error should no more occur now.

3.5.5

Do not fail with error Cannot read property 'asArrayBuffer' of undefined when having a docx with image (containing absolute Target for images, which might have been generated by some other docxtemplater module or some other program).

Also import correct xmlns attributes from imported document.

3.5.4

Declare supportedFileTypes, which allows to use this module with the new docxtemplater constructor which was introduced in docxtemplater 3.17.

3.5.3

Bugfix "Cannot read property 'appendChild' of undefined" when including a document where the {:include tag} is followed by a page break

3.5.2

Make it possible to have pagebreaks between two includes, without removing the pagebreak.

3.5.1

Fix issue Cannot read property 'getAttribute' of undefined when having unnamed styles in main document.

3.5.0

  • Add numId from styles.xml so that numbering values are better copied

  • Include w:sdt tags in addition of w:p tags

3.4.8

  • Handle numeric style ids by using name for the comparison if the id exists.

3.4.7

  • Fix Memory Leak when including document containing many images.

3.4.6

  • Bugfix : add possibility to include image that is within a table

3.4.5

  • Update browser build to use XMLSerializer instead of xmldom

  • Use requiredAPIVersion

3.4.4

  • Move docxtemplater from devDependencies to dependencies

Explanation : On some versions of npm (notably 5.8.0), when having a package containing docxtemplater-subtemplate-module, the installation will generate a tree of node_modules that puts the module on a level where it has no access to docxtemplater. By explicitly asking it as a dependency, this issue is avoided.

3.4.3

Fix bug when using equations and including templates 2 levels deep (with parent.docx /son.docx and grand-son.docx which contains an equation).

3.4.2

  • Make module compatible with docxtemplater version 3.5 and below.

Explanation : Recently, the scopemananger API (internal API) has changed, this new version of the subtemplate module makes the module work with both versions newer than 3.6 and older than 3.6 of docxtemplater.

3.4.1

Add support for adding arrays of subtemplates with subsections.

When using the SubsectionModule, make it possible to replace the first section of the including document and replace the last section of the included document (this avoids additional pagebreaks).

3.4.0

Add support for adding subsections with headers by using subsection module.

3.3.0

Add possibility to add sections from subdocument if .includeSections is true

3.2.5

Add meta context argument to custom parser with information about the tag for each types of tags

3.2.4

Add support for including documents with pagebreaks

3.2.3

Fix corruption when including equations

3.2.2

Add support for including charts refering to Excel documents

3.2.1

Add support for including documents containing Excel documents (OLEObjects)

3.2.0

Add support for including documents containing comments

3.1.9

Avoid corruption when including multiple charts with subtemplates

3.1.8

Bugfix corruption when including document with chart

3.1.7

Add support for including documents containing charts

3.1.6

Remove error getElementsByTagName when word/numbering.xml doesn't exist in main document

3.1.5

Fix bug when subtemplate contains list coming from an other module (HTML)

3.1.4

Images in the subtemplate when there are already some images in the headers does not cause a corruption anymore.

3.1.3

Links in the subtemplate are now handled correctly

3.1.2

Fix corruption on some word version because of duplicate styles

3.1.1

Add support for lists in subtemplates (ordered lists and unordered lists).

3.1.0

Import styles in the case when the included document defines some styles.

3.0.1

Handle images importing

3.0.0

Initial release

Talk with sales Contact us