Git Product home page Git Product logo

xlsx-template's Introduction

XLSX Template

Build status

This module provides a means of generating "real" Excel reports (i.e. not CSV files) in NodeJS applications.

The basic principle is this: You create a template in Excel. This can be formatted as you wish, contain formulae etc. In this file, you put placeholders using a specific syntax (see below). In code, you build a map of placeholders to values and then load the template, substitute the placeholders for the relevant values, and generate a new .xlsx file that you can then serve to the user.

Placeholders

Placeholders are inserted in cells in a spreadsheet. It does not matter how those cells are formatted, so e.g. it is OK to insert a placeholder (which is text content) into a cell formatted as a number or currecy or date, if you expect the placeholder to resolve to a number or currency or date.

Scalars

Simple placholders take the format ${name}. Here, name is the name of a key in the placeholders map. The value of this placholder here should be a scalar, i.e. not an array or object. The placeholder may appear on its own in a cell, or as part of a text string. For example:

| Extracted on: | ${extractDate} |

might result in (depending on date formatting in the second cell):

| Extracted on: | Jun-01-2013 |

Here, extractDate may be a date and the second cell may be formatted as a number.

Inside scalars there possibility to use array indexers. For example:

Given data

var template = { extractDates: ["Jun-01-2113", "Jun-01-2013" ]}

which will be applied to following template

| Extracted on: | ${extractDates[0]} |

will results in the

| Extracted on: | Jun-01-2113 |

Columns

You can use arrays as placeholder values to indicate that the placeholder cell is to be replicated across columns. In this case, the placeholder cannot appear inside a text string - it must be the only thing in its cell. For example, if the placehodler value dates is an array of dates:

| ${dates} |

might result in:

| Jun-01-2013 | Jun-02-2013 | Jun-03-2013 |

Tables

Finally, you can build tables made up of multiple rows. In this case, each placeholder should be prefixed by table: and contain both the name of the placeholder variable (a list of objects) and a key (in each object in the list). For example:

| Name                 | Age                 |
| ${table:people.name} | ${table:people.age} |

If the replacement value under people is an array of objects, and each of those objects have keys name and age, you may end up with something like:

| Name        | Age |
| John Smith  | 20  |
| Bob Johnson | 22  |

If a particular value is an array, then it will be repeated across columns as above.

Images in cell

โš ๏ธ Warning : This functionality is avaible only from new Excel version (2308 - maybe more recent but 2302 does not work).

images in cell automatically match cell size (merge cell size) and formatted cell (alignment, colorization etc.)

You can insert images in cell with
| My image: | ${imageincell:imageName} |

Given data

var template = { imageName: "helloImage.jpg"}

You can insert a list of images with

| My images | ${table:images.name:imageincell} |

Given data

var template = { images: [{name : "helloImage1.jpg"}, {name : "helloImage2.jpg"}]}   

support the same format as 'image' just below

Images

You can insert images with

| My image: | ${image:imageName} |

Given data

var template = { imageName: "helloImage.jpg"}

You can insert a list of images with

| My images | ${table:images.name:image} |

Given data

var template = { images: [{name : "helloImage1.jpg"}, {name : "helloImage2.jpg"}]}

Supported image format in given data :

  • Base64 string
  • Base64 Buffer
  • Absolute path file
  • relative path file (absolute is prior to relative in test)
  • URL : TODO

You can pass imageRootPath option for setting the root folder for your images.

var option = {imageRootPath : "/path/to/your/image/dir"}  
...  
var t = new XlsxTemplate(data, option);

If the image Placeholders is in standard cell, image is insert normaly
If the image Placeholders is in merge cell, image feet (at the best) the size of the merge cell.

You can pass imageRatio option for adjust the ratio image (in percent and for standard cell - not applied on merge cell)

var option = {imageRatio : 75.4}  
...  
var t = new XlsxTemplate(data, option);

Generating reports

To make this magic happen, you need some code like this:

    var XlsxTemplate = require('xlsx-template');

    // Load an XLSX file into memory
    fs.readFile(path.join(__dirname, 'templates', 'template1.xlsx'), function(err, data) {

        // Create a template
        var template = new XlsxTemplate(data);

        // Replacements take place on first sheet
        var sheetNumber = 1;

        // Set up some placeholder values matching the placeholders in the template
        var values = {
                extractDate: new Date(),
                dates: [ new Date("2013-06-01"), new Date("2013-06-02"), new Date("2013-06-03") ],
                people: [
                    {name: "John Smith", age: 20},
                    {name: "Bob Johnson", age: 22}
                ]
            };

        // Perform substitution
        template.substitute(sheetNumber, values);

        // Get binary data
        var data = template.generate();

        // ...

    });

At this stage, data is a string blob representing the compressed archive that is the .xlsx file (that's right, a .xlsx file is a zip file of XML files, if you didn't know). You can send this back to a client, store it to disk, attach it to an email or do whatever you want with it.

You can pass options to generate() to set a different return type. use {type: 'uint8array'} to generate a Uint8Array, arraybuffer, blob, nodebuffer to generate an ArrayBuffer, Blob or nodebuffer, or base64 to generate a base64-encoded string.

Caveats

  • The spreadsheet must be saved in .xlsx format. .xls, .xlsb or .xlsm won't work.
  • Column (array) and table (array-of-objects) insertions cause rows and cells to be inserted or removed. When this happens, only a limited number of adjustments are made:
    • Merged cells and named cells/ranges to the right of cells where insertions or deletions are made are moved right or left, appropriately. This may not work well if cells are merged across rows, unless all rows have the same number of insertions.
    • Merged cells, named tables or named cells/ranges below rows where further rows are inserted are moved down. Formulae are not adjusted.
  • As a corollary to this, it is not always easy to build formulae that refer to cells in a table (e.g. summing all rows) where the exact number of rows or columns is not known in advance. There are two strategies for dealing with this:
    • Put the table as the last (or only) thing on a particular sheet, and use a formula that includes a large number of rows or columns in the hope that the actual table will be smaller than this number.
    • Use named tables. When a placeholder in a named table causes columns or rows to be added, the table definition (i.e. the cells included in the table) will be updated accordingly. You can then use things like TableName[ColumnName] in your formula to refer to all values in a given column in the table as a logical range.
  • Placeholders only work in simple cells and tables, pivot tables or other such things.

Changelog

Version 1.4.4

  • Move hyperlinks references on added rows and columns. (#184). Thanks @IagoSRL
  • Fix line issue under table with merged cell. (#188). Thanks @muyoungko

Version 1.4.3

  • Fix potential issue when template has lot of images.
  • Update image-size to 1.0.2

Version 1.4.2

Version 1.4.1

Version 1.4.0

  • substituteAll: Interpolate values for all the sheets using the given substitutions (#173) Thanks @jonathankeebler
  • intandfloatdon't exist in Typescript, both are of typenumber`. This fixes it. (#169) Thanks @EHadoux
  • Insert images. (#126). Thanks @jdugh
  • Add customXml in the order list for rebuild. (#154). Thanks @jdugh
  • Adding 2 options affect table substitution : subsituteAllTableRow and pushDownPageBreakOnTableSubstitution. (#124). Thanks @jdugh

Version 1.3.2

  • Fix import statement for jszip

Version 1.3.1

  • Added the imageRatio parameter like a percent ratio when insert images. (#121)
  • Add new substitution for images. (#110)
  • Fixing Defined Range Name with Sheet Name. (#150)
  • Add binary option for copySheet : for header/footer in UTF-8 (#130)

Version 1.3.0

  • Added support for optional moving of the images together with table. (#109)

Version 1.2.0

  • Specify license field in addition to licenses field in the package.json (#102)

Version 1.1.0

  • Added TypeScript definitions. #101
  • NodeJS 12, 14 support

Version 1.0.0

Nothing to see here. Just I'm being brave and make version 1.0.0

Version 0.5.0

  • Placeholder in hyperlinks. #87
  • NodeJS 10 support

Version 0.4.0

  • Fix wrongly replacing text in shared strings #81

Version 0.2.0

  • Add ability copy and delete sheets.

Version 0.0.7

  • Fix bug with calculating when adding columns

Version 0.0.6

  • You can now pass options to generate(), which are passed to JSZip
  • Fix setting of sheet when growing the sheet
  • Fix corruption of sheet when writing dates
  • Fix corruption of sheet when calculating calcChain

Version 0.0.5

  • Mysterious

Version 0.0.4

Merged pending pull requests

  • Deletion of the sheets.

Version 0.0.3

Merged a number of overdue pull requests, including:

  • Windows support
  • Support for table footers
  • Documentation improvements

Version 0.0.2

  • Fix a potential issue with the typing of string indices that could cause the first string to not render correctly if it contained a substitution.

Version 0.0.1

  • Initial release

xlsx-template's People

Contributors

adepanges avatar antfu avatar aserus avatar benjamin-chang avatar cartuchogl avatar dandalf avatar dependabot[bot] avatar ehadoux avatar iagosrl avatar jdugh avatar jonathankeebler avatar kant2002 avatar lstradella avatar m4fg avatar mhxbe avatar michacom avatar mrothberg avatar muyoungko avatar optilude avatar orthographic-pedant avatar rickbergfalk avatar ruanitto avatar rw-diefbell avatar sanderd17 avatar stevds avatar t-rav avatar tiholic avatar trysound avatar wombat86 avatar yenesey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xlsx-template's Issues

Adjust columns widths to match

Love the idea and implementation, one thing I ran into is that the columns widths need to be adjusted to match the strings inserted in a table or array. I have not yet found a proper way to do that.

The only thing that I came up with is that in the worksheets xml, there can be cols and col elements that define a custom with for the column. e.g:

<cols>
    <col min="6" max="6" width="27.7109375" bestFit="1" customWidth="1"/>
</cols>

The value is a number of "characters", which is basically the with of a number. For non-propertional fonts, this may be something else than the real with of a string, but it's a first approximation. I could live with counting the number of characters and adding a few for safety and hoping that it's not all W's

Does anyone know of an "auto width" setting that would remove the need to recalculate the column width needed? I'd rather just set this in the template than having to adjust it in code.

Note that I am not really asking for a solution in xlsx-template, just a way to get it working, as I can imagine more people will have to deal with this problem here. I am ok with having to write some code to further manipulate the worksheet xml

Documentation improvement: how to write file

When I tried this at first, I always received corrupt files. I opened it with a text editor, and saw a lot more XML than I'd expect in a zip.

The reason was that I used fs.writeFile(fileName, data) instead of fs.writeFile(fileName, data, 'binary').

It might be worth mentioning this in the docs

proposal: scopes by named ranges

I'd like to propose adding scopes to xlsx-template. This would introduce a lot more flexibility, like deeper and repeated structures. I think one way to do this is by using named ranges. Here, I'd like to discuss it a bit before taking a stab at implementing it.

I like to hear your opinions, objections, use cases and questions.

scopes by named ranges

If we have a json structure like so:

{
    scalar: 'foo',
    array: [1, 2, 3],
    table: [
        {row: 1, value: 'a'},
        {row: 2, value: 'b'},
        {row: 3, value: 'c'}
    ],
    deeper: {
        scalar: 'foo',
        array: [1, 2, 3],
        table: [
            {row: 1, value: 'a'},
            {row: 2, value: 'b'},
            {row: 3, value: 'c'}
        ],
    },
    nested: {
        deeper: {
            scalar: 'foo',
            array: [1, 2, 3],
            table: [
                {row: 1, value: 'a'},
                {row: 2, value: 'b'},
                {row: 3, value: 'c'}
            ],
        },
        table: [
            {
                name: 'lowercase',
                data: [
                    {row: 1, value: 'a'},
                    {row: 2, value: 'b'},
                    {row: 3, value: 'c'}
                ]
            }, {
                name: 'uppercase',
                data: [
                    {row: 1, value: 'A'},
                    {row: 2, value: 'B'},
                    {row: 3, value: 'C'}
                ]
            }
        ]
    }
}

We can already handle the scalar, the array and the table. Displaying the array vertically is currently not possible, but that is easily fixed by using {$table:array} and a small amendment.

The deeper.scalar value can also be used, also, I think deeper.array can be fixed, but handling deeper.table is not trivial, as we would not know where to split the path. However, this may also be fixed by descending into an object as long as possible and use the rest of the path to descend in the array of objects.

Multiple tables is not currently possible.

A named range in excel can contain dots, which is convenient. This would allow us to define a scope for a range of cells. For example, if we have a range named

\deeper

And a cell within that range with the value

${scalar}

We can piece together a working substitution. This also goes for ${array} and ${table} within \deeper. This would also work if we name the range \nested.deeper

If a scope resolves to an array, like \nested.table, we can repeat the whole range and apply the template within to every structure in the array. This makes excel templates very flexible.

Use cases

simpler tables

Although not directly needed, a table can be made simpler. Rather than having a cell with ${table:table.row} we can now use a range \table, resolving a scope to the table array. As such, the whole range structure would be repeated vertically. The cells within can now be simple scalars like ${row}. Moreover, as they are now treated as scalars, you could use them in a string: row ${row} has value ${value}

tables from deeper structures

From the above example, using deeper.table is just as easy, by renaming the range to \deeper.table

repeating tables

Suppose we have a template like this:
image

This would result in two tables:
image

Caveats

One problem is that names must be unique. This may be mitigated by smart naming in the json structure, and by avoiding single step scopes (like \data above). It is still a problem I have no solution for though

How would we prevent accidental naming clashes with user defined ranges? I suppose by prepending the \ character, chances of collision are rather small, but still...

Other thoughts

Excel also allows for an underscore at the start of a range name. We may create two variants, one that repeats vertically (\nested.table), one that repeats horizontally: _nested.table. The latter would result in this:

image

A way to avoid using named ranges (and the uniqueness problem) is to just put the data in a cell, for example like so:

$${B8:C11:\nested.table}

And then just delete the contents of such cells. (we do have some more flexibility for the syntax then). Also, we may have a special meta range that span some rows that contain meta info for the template like the above and have that entire set of rows removed on substitution. For example like this:

image

Next steps

Do you see any other problems? Or do you have interesting use cases to add that may or may not be solved with this proposal? Would there be another way in excel to define a scope on a range that does not have the uniqueness problem?

Would xlsx-template be the right codebase to put this in or should I create something else entirely? I do have a need for repeated tables and I like the way xlsx-templates are super-maintainable. I also feel I know enough of xml, js and excel to implement this.

Ability to dynamically add formula into cell

Is there a way to specify a formula for a cell explicitly? I have a value of something like this in my data:
"formula": "=SUM(B12:B26)"

It correctly puts it in the generated spreadsheet as a formula value, but it doesn't actually trigger calculating the value until I go into the cell and hit the enter key.

Copy Sheet doesn't work

Hello!
I use v0.4.0
I tried to CopySheet
In particular:

JSZipUtils.getBinaryContent('/template.xlsx', (error: Error | undefined, content: string) => {
        if (error) {
                throw error;
        }
        const template = new XlsxTemplate(content);
        template.copySheet('BaseSheet', 'LookAtMe1');
        template.substitute('BaseSheet', params);
        template.copySheet('BaseSheet', 'LookAtMe2');
        const data = template.generate({ type: 'arraybuffer' });
        ...
});

However, result is sad

  1. The copied sheets ('LookAtMe1', 'LookAtMe2') are both blank
  2. First sheet ('BaseSheet') loses its design (column widths change, cell borders become visible, background color is lost, and so on)

insert columns

Please tell me how to insert columns (month_1, ..., month_N) from an array if their number is initially unknown?
The number of columns I can have from 1 to N, as well as rows in each column
My array looks like this:

marray: [
		0: {
			month_1: {
					name: [
						count: 1,
						price: 10,
						sum: 10
					],
					name_1: [
						count: 3,
						price: 12,
						sum: 36
					],
					name_2: [
						count: 2,
						price: 8,
						sum: 16
					],
					...: [
					],
					name_N: [
						count: 1,
						price: 15,
						sum: 15
					]
			}
		}
		1: {
			month_2: {
					name: [
						count: 2,
						price: 6,
						sum: 12
					],
					name_1: [
						count: 1,
						price: 13,
						sum: 13
					],
					name_2: [
						count: 4,
						price: 10,
						sum: 40
					],
					...: [
					],
					name_N: [
						count: 3,
						price: 11,
						sum: 33
					]
			}
		},
		2: {
			...: {
					...
			}
		},
		...: {
			...: {
			}
		},
		N: {
			month_N: {
					name: [
						count: 1,
						price: 7,
						sum: 7
					],
					name_1: [
						count: 2,
						price: 15,
						sum: 30
					],
					name_2: [
						count: 2,
						price: 8,
						sum: 16
					],
					...: [
					],
					name_N: [
						count: 5,
						price: 6,
						sum: 30
					]
			}
		}
			
		]

template.xlsx

as a result, the data file should be obtained by example 'template.xlsx'

Accessing array elements

It would be nice if I could pass in an array and simply do ${foo[0]} in the template. Right now, for my use case, I have to do this in my code:

const templateData = {
   foo0: foo[0],
   foo1: foo[1],
   ...
}

How to download file?

Hi,

How to download file from server.....and.....how to retrieve in the frontend....

  // Get binary data backend
      var data = template.generate();
      res.setHeader('Content-Type', 'application/vnd.openxmlformats');
        res.setHeader("Content-Disposition", "attachment; filename=" + "Report.xlsx");
        res.end(data, 'binary');

//Frontend...
Get ajax?

"undefined" substituted for missing JSON value

I love this project! Is there a way to prevent having "undefined" substituted for a missing value? I would like to generate a report where some of the columns may have data, and others it may not apply so they should be empty.

Header / Footer variables

I have just tested this module and it seems to be working great. The only issue I have found that is not working is when I use ${someVariable} in the header or footer of an XLSX file, all that appears is is the variable name ... ${someVariable}. should the substitution be working in headers and footers? If not, I would like to request that as a feature / enhancement if that is possible.

I want nested array of object like this........

 var values = {
                     people: [
                          {
                             vendorType: 'Decorator', name: "", age: 0, city: '',
                             data: [{ vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             ] },
                         {
                             vendorType: 'Decorator', name: "", age: 0, city: '',
                             data: [{ vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             ]},
                         {
                             vendorType: 'Decorator', name: "", age: 0, city: '',
                             data: [{ vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             { vendorType: '', name: "Bob Johnson", age: 22, city: 'mumbai' },
                             ]},
                         { 
                            vendorType:'Decorator',name: "", age:0,city:'',
                     data: [{vendorType:'',name: "Bob Johnson", age: 22,city:'mumbai'},
                           {vendorType:'',name: "Bob Johnson", age: 22,city:'mumbai'},
                           {vendorType:'',name: "Bob Johnson", age: 22,city:'mumbai'},
                    
                     
                           ]}
                            ],
                 };

please help me

Can I use this library on Windows

Hi,
as per issues #1 and #2, this library had some problems in the past when used in Windows. My question is: were those problems solved? and, if they were, does the last version on npm, 0.0.3, contain this correction?

I'm doing a very simple sample but with no success. Excel always refuses to open the resulting file saying that the file is damaged and can't be opened.

I really need this library and it will help me a lot in developing my project; it is just I haven't been able to have it working (in Windows). Many thanks and bye ...

Is this still supported ?

Looks like a good idea especially if template preserves formatting so Pivots etc still work when data swapped out.

Help repeating cell validation per row

Dear Team,

I am populating xlsx from an array of objects, it works perfect. And I also define a cell validation drop down list too as the last column of the XSLS template.
After the generation, only the 1st row has the drop down list in place, but not the subsequent row.... any hack I can try?
image

Thank you very much.

Documentation improvement: download file

I was trying to create a file only in memory and download it, and it took me hours to figure it out, i couldn't find anything even in Stack Overflow and lots of programming blogs elsewhere, but when "give up" is not an option... you get there.
i'm suggesting to you guys to just improve a little more the documentation
and thanks a lot for your work, it saved my day.

the following lines of code worked fine on the framework i'm using for nodejs

var fs = require("fs");
var path = require("path");
var XlsxTemplate = require('xlsx-template');

//...

try{
/* use only fs.readFileSync */
let xlsxTemplate = new XlsxTemplate(fs.readFileSync(path.resolve(__dirname, file_path))) // <-
let sheetNumber = 1;
let values = {
data: some_data,
arr: some_array
}
xlsxTemplate.substitute(sheetNumber, values);
// use only "nodebuffer"
let encode = { type: 'nodebuffer' };
let buffer = xlsxTemplate.generate(encode)

//expected return in the framework
return  [
    buffer,
   "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
];

}catch(error){
let e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
};
console.log(JSON.stringify({ error: e }));
throw error;
}

Supporting Compression - JSZip

Hi, I just made some tests with this library, and it seems pretty good, but I'm having issues generating excel files that are huge in rows (500k to 1m rows).

I've tried enabling compression, but Excel does not open the resulting file, saying it has an invalid extension.

The lines I've used:

    var newData = t.generate({
        compression: "DEFLATE",
        compressionOptions: {
            level: 3
        }
    });

I've tried to just let compression: "DEFLATE" as well, no sucess.

escaped unicode char breaks excel generation

hi,

if you print a string containing an escaped unicode character (e.g. \u0010), the generated excel is corrupted. atm i will strip any unicode character in order to make it working but it would be nice have utf-8 support or automatic stripping of unicode chars.

Table with an array of 1 causes problem

t.substitute(1, {
    titles: ['title1', 'title2'],
    planData: [
        { name: 'A', role: ['role1'] },
        { name: 'B', role: ['role2'] }
    ]
});

With a input like this

image

Would result a file which excel would complain upon opening.
This is due to duplicate element inside sheet1.xml.

    <row ht="14" r="2" spans="1:7" x14ac:dyDescent="0.2">
      <c r="A2" t="s">
        <v>6</v>
      </c>
      <c r="B2" s="6" t="s">
        <v>8</v>
      </c>
      <c r="B2" s="6" t="s">
        <v>3</v>
      </c>
    </row>

Placeholder in table header names may lead to corrupted generated xlsx file

Using an object to substitute placeholders in table header names results in an Excel file that needs to be repaired while the replacement is done. Templates are joint.

This works fine:

// Load an XLSX file into memory
fs.readFile('./template2.xlsx', function (err, data) {

    // Create a template
    var template = new XlsxTemplate(data);

    // Replacements take place on first sheet
    var sheetNumber = 1;

    // Set up some placeholder values matching the placeholders in the template
    var values = {
        columnName: 'Will have to be repaired'
    };

    // Perform substitution
    template.substitute(sheetNumber, values);

    // Get binary data
    var fileData = template.generate();

    fs.writeFileSync('./output2.xlsx', fileData, 'binary');
});

This doesn't:

// Load an XLSX file into memory
fs.readFile('./template1.xlsx', function (err, data) {

    // Create a template
    var template = new XlsxTemplate(data);

    // Replacements take place on first sheet
    var sheetNumber = 1;

    // Set up some placeholder values matching the placeholders in the template
    var values = {
        column: { name: 'Will have to be repaired'}
    };

    // Perform substitution
    template.substitute(sheetNumber, values);

    // Get binary data
    var fileData = template.generate();

    fs.writeFileSync('./output1.xlsx', fileData, 'binary');
});

template1.xlsx
template2.xlsx

proplem : .join(self.prefix, '_rels', path.basename(workbookPath) + '.rels')).asText()).

my english no good,so pls try understand my word.

I learn a nodejs , build a project,I need a module export xlsx file, and I find xlsx-template ,
but something dont work and I dont work , and I cant fix,
so I find your contact , write a email , hope you can help me, thx!

the error msg:

.join(self.prefix, '_rels', path.basename(workbookPath) + '.rels')).asText()).
                                                                    ^
TypeError: Cannot call method 'asText' of null
    at module.exports.Workbook.loadTemplate (E:\WEB\model-chooese\node_modules\x
lsx-template\lib\index.js:50:131)
    at new module.exports.Workbook (E:\WEB\model-chooese\node_modules\xlsx-templ
ate\lib\index.js:27:18)
    at E:\WEB\model-chooese\app.js:56:20
    at fs.js:266:14
    at Object.oncomplete (fs.js:107:15)

and my code like this:

app.get('/api/save/:mark',function(req,res){
  var mark = req.params.mark;
  var file = __dirname+'\\public\\'+mark+'.xlsx';
  console.log(file);
  fs.readFile(file, function(err, data) {

    // Create a template
    console.log(data);
    var template = new XlsxTemplate(data);          //here is line 56
    console.log(template);

    // Replacements take place on first sheet
    var sheetNumber = 1;

    // Set up some placeholder values matching the placeholders in the template
    var values = {
      extractDate: new Date(),
      dates: [new Date("2013-06-01"),
            new Date("2013-06-02"),
            new Date("2013-06-03")
      ],
      people: [{
        name: "John Smith",
        age: 20
      }, {
        name: "Bob Johnson",
        age: 22
      }]
    };

    // Perform substitution
    template.substitute(sheetNumber, values);

    // Get binary data
    var data = template.generate();
    console.log(data);
    // ...

    // here , I wanna download this file , how can I do? what is  template.generate() return object?
  });


  //res.download(__dirname+'/public/'+mark+'.xls',mark+'.xls');
})

Is this project abandoned?

@optilude if you don't have time anymore to keep an eye on this project, perhaps you could appoint some collaborers to keep the project alive.

I believe it's awesome and solves a functionality that every other XLSX manipulation library is missing right now.

Currently, I've had to resort to @woodenfish fork to adress the issue of appending merged cells to a table, which is the only showstopper for me to use the main branch.

It would be neat if that and the other PRs could be merged sometime soon.

formula scalar

Is it possible to define the scalar as an Excel formula? For example, I defined the JSON { a: '=concatenate("A1")' } and put the placeholder "${a}" in Excel template.

Get same value in diff sheet

I have a ${page} which outputs page number for every sheet.
Problem is every sheet ${page} would be the same.

In the subsititueScalar, It replace String for sharedStrings.

// Perform substitution of a single value
    Workbook.prototype.substituteScalar = function(cell, string, placeholder, substitution) {
        var self = this;

        if(placeholder.full && typeof(substitution) === "string") {
            self.replaceString(string, substitution);
        }

        ...

    };

So it would not substitute again because the string in sharedStrings is already replaced at first time.

When I comment replaceString in function substituteScalar, this problem is fixed.

Any Idea?

Thanks.

Charts not copied

Hello,
I'm using the v0.4.0
The copySheet did not copy the charts!

Single entry in array not shown in xls

Situation: I need to show one or multiple data entries in an xls.
When I have 2 or more entries, everything is perfect, xls gets rendered with the correct headers in the columns. When I have 1 entry, only the headers are generated in the xls.

Current workarround: When the array.length == 1, add an empty entry to the array. In the xls only one entry is shown because the second entry is empty and we can't really check if it's there.
if (filteredEvents.length == 1) { this.addEmptyEvent(filteredEvents); }

Please help.

Table replicated formulae saved as text instead of formulae

not sure if this is a restriction of both excel and google sheets needing to have a special event action to mark a cell as being a formulae, but I hit this snag:

In my sheet template, I have a cell with this value: ${table:fills.usdChange}

in the values I pass in to substitute, I have an array of fills with an object keypair like this:

...
fills: [{
...
usdChange: '=(E4*D4)+F4'
}]
...

when I execute and open the sheet in excel or google spreadsheets, the value of the repeated columns is '=(E4*D4)+F4' instead of a formulae executing that function.

Is there another way I should be tagging the spreadsheet or the fill value in order to id this as a formulae?

dimension is null (Cannot read property 'attrib' of null)

When trying to add a table with multiple rows I get this error:
TypeError: Cannot read property 'attrib' of null

traced down to this:

dimension = sheet.root.find("dimension")

dimension turns out to be null

TypeError: Cannot read property 'attrib' of null
    at Workbook.module.exports.Workbook.substitute (.../node_modules/xlsx-template/lib/index.js:248:59)

copySheet and deleteSheet selects all sheets

Hi

When I use template.copySheet or template.deleteSheet and open generated xlsx file with MS Excel, it opens with all-selected sheets:

grvtfhi

That means that if you don't choose another sheet, all sheets will change with current (if you in Excel set cell A1 = asd in current sheet, all A1 cells in another sheets well be set "asd")

Can you please fix it? I think this is because of template._rebuild method, which is calling after these methods.

Thx

no re-calculating

We set up a simple XLSX with two variables and then a cell that adds those two cells together. When we substitute the two variables and save the new file, the 3 third that has the formula didn't re-calculate.

Replacing with a 1 cell-sized array not working?

Hi,

First, thanks for the project, it's great :)

I'm trying to substitute a placeholder to put an array, but i've been in trouble when inserting a 1 cell array. Here is an exemple :
My excel template is just : | ${table:sales.payments} |
My code :

  var tpl = fs.readFileSync('tpl.xlsx');
  tpl = new xls(tpl);
  tpl.substitute(1, data);
  const result = tpl.generate();

The strange thing is, with data = { "sales": [ { "payments": [123,456], } ] };, it works perfectly, but with data = { "sales": [ { "payments": [666], } ] };, it does not replace the placeholder (${table:sales.payments} is written in the output file).

It also works for data = { "sales": [ { "payments": 123, } ] };

Is this a normal behaviour ? It's pretty annoying to have to check the length of my arrays and convert them when their length is 1...

Thanks !

Defined formulas are not calculated

In excel templates I define some formulas =sum(C10) but on the final generated file the formulas are equal to zero. After clicking the cell where formula was defined the value appears.
I have checked 'on' the option formula auto-calculation in my Excel and still it is not helping. I have to click all cells manually to trigger the calculations.

Dates corrupt file in Excel 2016 (v15.24) on Mac

I'm creating a table template with date column. After substitution is done and I open result in Excel I get the message saying that the file is corrupt and Excel proposes to try repairing this file.

When I investigated this issue for a while i noticed that Excel does not add "t" attribute for non-text cells (tested with numbers, dates, currency, percentage and general format) at all. I have a fix for this and am going to sign off a PR soon. Would be great if someone could test it in different version of Excel and also on Windows.

Write to merged cells

When I write tables, if ${table:people.name} is put in a merged cell, after calling template.substitute(), people.name is correctly placed in the merged cell only for the first row. For the following rows, people.name is placed in the 1st cell of the merged cell.

Any hint to solve that?

Dynamic Nested Header Support

Hello,

First of all, this is super helpful library. Thank you for your support. Anyway, I am wondering that how can I do dynamic headers(nested dynamic headers)?

For instance,

var values = {
    items: [{
    name: 'Drink',
    count: 3,
    brand: 'Pepsi'
  }, {
    name: 'Food',
    count: 2,
    brand: 'KFC'
  }, {
    name: 'Desert',
    count: 5,
    brand: 'CheeseCakeFactory'
 }]
}

and I want to print this array in this format:

Drink
count 3
brand Pepsi

Food
count 2
brand KFC

Desert
count 5,
brand CheesecakeFactory

I thought this way, but it did not work out:

${table:items.name}
${table:items.count}
${table:items.brand}

but it prints

Drink
Food
Desert
3
2
5
Pepsi
KFC
CheesecakeFactory

if this lib supports this feature, please let me know how can I do that? Thanks a lot for any kind of help!

using mssql stream and no function available

I am using using the mssql connector in node and have the stream option enabled. The reason being the record set getting returned is big (more than 60K) and I want the whole process of generating the excel consume less memory on the server and create the excel a little faster.

Now when calling the template.substitute function it basically removes the placeholders and then all the rows that are returned from the stream are not populated in the final excel output. I was wondering if there is a function which will allow to do so.
Until then the workaround that I have is keep the adding the placeholder back in the excel until the last row is retrieved.

lmk if you need more info.

Add image into template

Hi @optilude ,
This is a useful package.
I'm very happy to use it, but I want to add an image into the template, so I have to use another package so this does not interest me, so I hope to support more images.
Please help me!
Thanks for the support!

Implement in tables {people: {users: [{name: "John Smith"}]}}

    var values = {
        people: [
            {
                name: 'John Smith',
                age: 20,
                test: { name: 'Smith' },
            },
            {
                name: 'Bob Johnson',
                age: 22,
                test: { name: 'Johnson' },
            },
        ],
        people: {
            users: [
                {
                    name: 'John Smith User',
                    age: 20,
                    test: { name: 'Smith' },
                },
                {
                    name: 'Bob Johnson User',
                    age: 22,
                    test: { name: 'Johnson' },
                },
            ],
            admins: [
                {
                    name: 'John Smith Admin',
                    age: 20,
                    test: { name: 'Smith' },
                },
                {
                    name: 'Bob Johnson Admin',
                    age: 22,
                    test: { name: 'Johnson' },
                },
            ],
        },
    };

So for example I have this object values that I want to put into template.

${table:people.name} - this placeholder works
${table:peopleGroup.users.name} - this does not work, in result value is blank

How to download directly without write file on disk?

I tried to download from an angularjs application an excel file (xslx) generated thorough a nodejs application with this library. The data is sent in binary form; I don't save the file on the server, but directly send the stream of data in the express response.

This is the server-side snippet code

var template = new XlsxTemplate(data);
var sheetNumber = 1;
var values = {};
template.substitute(sheetNumber, values);
var blobExport = template.generate();
return res.send(new Buffer(blobExport, 'binary'));

and this is the client-side snippet code

var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
var blob = new Blob([blobData], {type: contentType});
var urlObj = (window.URL || window.webkitURL);
var url = urlObj.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();

the file is getting downloaded but it seems to be corrupted. Is this a bug or is there a workaround to it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.