Add VBA script to file I am creating from scratch
anathebealo opened this issue · comments
I'm working on a program that will generate an excel spreadsheet. I have two goals:
- Add data validation drop-downs to cells in sheet1 and use columns of data in sheet2 as the list of available values.
- Add a VBA macro that allows me to "multi-select" dropdown selections into a comma separated list.
Looks like data validations are a pro feature, but macros have some support in the community version. Is there a way to attach a VBA macro to an excel file that I am generating in the browser? I have a VBA blob that I want to attach, and then save the output file as a xlsm file. Looks like all mentions of macros are related to reading files in and then doing something with the attached macros, though.
I found an old issue that says it wasn't possible at the time...I assume this hasn't changed?
General flow of my generate workbook function:
const onClick = () => {
try {
const workbook = xlsx.utils.book_new();
const lookupValues = generateLookupValueRows();
const lookupValuesSheet = xlsx.utils.json_to_sheet(lookupValues);
xlsx.utils.book_append_sheet(workbook, lookupValuesSheet, `Lookup Values`);
const templateRows = generateTemplateRows();
const templateSheet = xlsx.utils.json_to_sheet(templateRows);
xlsx.utils.book_append_sheet(workbook, templateSheet, `Template Input`);
// want to attach a VBA blob to the workbook before writing the file
xlsx.writeFile(workbook, `Template.xlsm`);
} catch (err) {
console.log('Error downloading template');
}
};
Thanks!
https://docs.sheetjs.com/docs/csf/features/#vba-and-macros
You can't add to the XLSX target since XLSX does not support macros. You can add to XLSM or XLSB or XLS.
To extract the macro blob, run this once in NodeJS to generate vba.bin
:
var XLSX = require("xlsx"), fs = require("fs");
// this part is from the docs
var wb = XLSX.readFile("path/to/template/file", { bookVBA: true });
var vbablob = wb.vbaraw;
// save to file
fs.writeFileSync("vba.bin", vbablob);
Put the generated vba.bin
somewhere that your browser can fetch (usually in a /public
directory), then make the marked changes:
const onClick = async() => { // <-- async function
try {
const workbook = xlsx.utils.book_new();
const lookupValues = generateLookupValueRows();
const lookupValuesSheet = xlsx.utils.json_to_sheet(lookupValues);
xlsx.utils.book_append_sheet(workbook, lookupValuesSheet, `Lookup Values`);
const templateRows = generateTemplateRows();
const templateSheet = xlsx.utils.json_to_sheet(templateRows);
xlsx.utils.book_append_sheet(workbook, templateSheet, `Template Input`);
// want to attach a VBA blob to the workbook before writing the file
var vba = new Uint8Array(await (await fetch("vba.bin")).arrayBuffer()) // <-- get vba blob
workbook.vbaraw = vba; // <-- add to file
xlsx.writeFile(workbook, `Template.xlsm`, {bookVBA: true}); // <-- use XLSM, set bookVBA true
} catch (err) {
console.log('Error downloading template');
}
};
I'm trying to achieve same results as this thread but vbablob = wb.vbaraw;
doesn't seem to be working
I'm manually saving the locally extracted vba.bin in S3 bucket and then trying to fetch it, to attach to the new excel workbook generated using const workbook = xlsx.utils.book_new();
On downloading the xlsm using writeFile, I'm unable to see the validation/ vba macros on the file
On debugging, I see that the vba.bin is in fact successfully added to the workbook.
My export function flow is same as suggested above with exception of reading file from s3
const onClick = async() => { // <-- async function
try {
const workbook = xlsx.utils.book_new();
const lookupValues = generateLookupValueRows();
const lookupValuesSheet = xlsx.utils.json_to_sheet(lookupValues);
xlsx.utils.book_append_sheet(workbook, lookupValuesSheet, `Lookup Values`);
const templateRows = generateTemplateRows();
const templateSheet = xlsx.utils.json_to_sheet(templateRows);
xlsx.utils.book_append_sheet(workbook, templateSheet, `Template Input`);
// want to attach a VBA blob to the workbook before writing the file
var vba = new Uint8Array(await (await fetch("s3 bucket public url/vba.bin")).arrayBuffer()) // <-- get vba blob
workbook.vbaraw = vba; // <-- add to file
xlsx.writeFile(workbook, `Template.xlsm`, {bookVBA: true}); // <-- use XLSM, set bookVBA true
} catch (err) {
console.log('Error downloading template');
}
};
Any pointers would be helpful! Thanks!