Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate columns for a table in a FOR loop #15

Closed
vdechef opened this issue Mar 22, 2018 · 15 comments
Closed

Generate columns for a table in a FOR loop #15

vdechef opened this issue Mar 22, 2018 · 15 comments

Comments

@vdechef
Copy link
Contributor

vdechef commented Mar 22, 2018

I cannot figure out how to generate columns in a FOR loop.
Using docx-templates I can generate rows for a table, using :

My table
+++FOR elem IN elements+++
+++= $elem.number +++ : +++= $elem.text +++
+++END-FOR elem+++

The output is something like:

My table
1 : element 1
2 : element 2
3 : element 3

But I cannot figure out how to generate columns, to obtain something like :

My table
1 : element 1 2 : element 2 3 : element 3

I tried :

My table
+++FOR elem IN elements+++ +++= $elem.number +++ : +++= $elem.text +++ +++END-FOR elem

but this generates empty cells and I get something like :

My table
1 : element 1 2 : element 2 3 : element 3
@guigrpa
Copy link
Owner

guigrpa commented Mar 22, 2018

It's a well-known current limitation: https://github.com/guigrpa/docx-templates/blob/master/ROADMAP.md

Let's hope this feature can get some love in the near future!

@jbuijgers
Copy link

I fixed this by splitting up the array in groups of size n using a 'chunk' function (something like this).

The table would look something like this:

My table
+++FOR elem IN chunk(elements, 3)+++
+++= $elem[0].value +++ +++= $elem[1].value +++ +++= $elem[2].value +++
+++END-FOR elem+++

@vdechef
Copy link
Contributor Author

vdechef commented Nov 26, 2018

@jbuijgers thanks. But my problem is with an unknown number of columns. When you know the number of columns in your table, you can iterate "vertically", as shown in your example. When you don't, you need to iterate "horizontally", and that's where problem occurs.

@tomspeed22
Copy link

Hello, Has this feature been released yet ? Thanks for the feedback

@jjhbw
Copy link
Collaborator

jjhbw commented Jun 20, 2020

Nope, i'm afraid it isn't an easy one. Would welcome a PR! If you want to give it a shot, i'll be happy to help.

This was referenced Nov 16, 2020
@robertoferreirajrr
Copy link

robertoferreirajrr commented Dec 3, 2020

Why not use HTML tables for dynamic columns? I have used it and works perfectcally. Using +++HTML injectTable()+++

@namirsab
Copy link

@jjhbw I'm looking at the code to try to implement this feature. I already have some ideas, but I'd appreciate some help :)

@jjhbw
Copy link
Collaborator

jjhbw commented Dec 16, 2020

@namirsab Great! Happy to look over any code you already have, but im afraid i cant offer you any pointers. I dont really know where to start either.

@namirsab
Copy link

Maybe @guigrpa can help? That'd be great!

@Enzojz
Copy link

Enzojz commented Feb 1, 2022

Possible to generate such a table with embedded HTML but not FOR loop

@robertoferreirajrr
Copy link

HTML has the altchunk problem... a colum feature would be great!

@thomaspeklak
Copy link

You can use https://github.com/guigrpa/docx-templates/tree/master#inserting-literal-xml and http://officeopenxml.com/WPtableGrid.php to get dynamic tables, but there are some gotchas:

  1. processLineBreaks should be better false, or your inserted string should not contain any
  2. the result is not a valid document. In my case there was a nesting of the table inside a <w:p> which results in the table not being rendered. But you can rather easily modify the document after it is generated from the template.

Maybe this helps anyone:

My table component templates:

const components = {
    table: (cols: number, rows: string[]) => `||<w:tbl>||
    ||<w:tblPr>||
    ||<w:tblStyle w:val="TableGrid"/>||
    ||<w:tblW w:w="5000" w:type="pct"/>||
    ||</w:tblPr>||
    ||<w:tblGrid>||
    ${Array(cols).fill('||<w:gridCol w:w="2880"/>||').join('\n')}
    ||</w:tblGrid>||
    ${rows.join('\n')}
    ||</w:tbl>||`,
    row: (cells: string[]) => `||<w:tr>||
    ${cells.join('\n')}
    ||</w:tr>||`,
    cell: (content: string | number) => `||<w:tc>||
    ||<w:tcPr>||
    ||<w:tcW w:w="2880" w:type="dxa"/>||
    ||</w:tcPr>||
    ||<w:p>||
    ||<w:r>||
    ||<w:t>||${content}||</w:t>||
    ||</w:r>||
    ||</w:p>||
    ||</w:tc>||`,
};

// can be used like this:
components.table(headers.length, [
    components.row(headers.map((h) => components.cell(h))),
    ...rows.map((r) => components.row(r.map((c) => components.cell(c))))

And this fixes the table nesting. It manipulates the docx (which is only a zip file) in memory.

import AdmZip from 'adm-zip';
import xmlJS from 'xml-js';

export const fixTableInParagraphNesting = (report: Uint8Array) => {
    const zip = new AdmZip(Buffer.from(report));

    const document = zip
        .getEntries()
        .find((e) => e.entryName === 'word/document.xml');
    if (!document) {
        return report;
    }

    const data = document?.getData().toString('utf8');
    const xml = xmlJS.xml2js(data, {compact: false});

    xml.elements[0].elements[0].elements
        .filter((e) => e.name === 'w:p')
        .forEach((p) => {
            const table = recursiveFind(p, 'w:tbl');
            if (!table) return;
            const index = xml.elements[0].elements[0].elements.findIndex(
                (e) => e === p
            );
            xml.elements[0].elements[0].elements[index] = table;
        });

    document.setData(Buffer.from(xmlJS.js2xml(xml)));

    return zip.toBuffer();
};

const recursiveFind = (element: any, name: string): any => {
    if (element.name === name) {
        return element;
    }

    if (element.elements) {
        for (const e of element.elements) {
            const result = recursiveFind(e, name);
            if (result) {
                return result;
            }
        }
    }
};

The table is still rather boring at this moment, but I am sure that you can somehow add a style to it. I still need to figure this out.

This is definitely not a solid implementation but at least it is a starting point

@abhinovpankaj
Copy link

chunk

Does this work, I get chunk not defined error when I try this.

@thomaspeklak
Copy link

Worked with my template. I extracted the code from my project. Could be that I missed some details in the extraction process

jjhbw pushed a commit that referenced this issue Apr 1, 2024
@jjhbw
Copy link
Collaborator

jjhbw commented Apr 1, 2024

@jjhbw jjhbw closed this as completed Apr 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants