Mendix + DOCX = MenDOCX!

Vinicius Strugata Ambrosio
5 min readDec 21, 2024

--

MenDOCX: using DOCX.JS with Mendix!

This article shows a very basic way to generate Microsoft Word reports from a Mendix-made Web application. To accomplish this, we will use the DOCX.JS javascript package.

DEV Environment

  • Studio Pro 10.12.9
  • Node.JS v20.16.0

Use Case

We will use an application that allows editing data about employees as a use case for this article.

Procedure

[ 1 ] Start Studio Pro and create a new project using the Blank Web App.

[ 2 ] Create a Persistent Entity called Employee with the following attributes:

Employee Entity

[ 3 ] Right-click on the Entity and use the Generate Overview Pages … to instruct Studio Pro to create the Overview and Edit pages for you. Add the Overview Page to the Navigation Menu.

[ 4 ] Manually create some Employees, so that you have some data to export to DOCX. At this point, our app should resemble as shown below:

Employee Overview

[ 5 ] Now let’s add the code to export this data to DOCX. Create a Java Script Action called JSA_EmployeeList_ExportToDocx. This JSA must receive a list of Employees as a parameter.

JavaScriptAction parameters

[ 6 ] Open a PowerShell Terminal in the folder in which the Java Script Action was created <your project name>\javascriptsource\<your app module>\actions. In my case: D:\work\mx_docx\javascriptsource\app\actions

[ 7 ] Execute the command below to install the dependencies required to generate the DOCX file. These dependencies are the DOCX.JS and the FileSave.JS packages.

npm i docx file-saver

[ 8 ] Now let’s write the JS code that will generate the DOCX file from our data. Once the dependencies are in place, we can import them into our code (the full code will be shown later in this article):

import { AlignmentType, Document, Packer, Paragraph, Tab, TableCell, Table, TableRow, TextRun, HeadingLevel } from "docx";
import { saveAs } from "file-saver";

[ 9 ] The code below creates the basic table header:

const tableHeaderRow = new TableRow({
tableHeader: true,
children: [
new TableCell({
children: [
new Paragraph({
children: [new TextRun({
text: "First Name",
bold: true,
})]
})],
}),
new TableCell({
children: [
new Paragraph({
children: [new TextRun({
text: "Last Name",
bold: true,
})]
})],
}),
new TableCell({
children: [
new Paragraph({
children: [new TextRun({
text: "Gender",
bold: true,
})]
})],
}),
new TableCell({
children: [
new Paragraph({
children: [new TextRun({
text: "Job Title",
bold: true,
})]
})],
}),
new TableCell({
children: [
new Paragraph({
children: [new TextRun({
text: "Age",
bold: true,
})]
})],
}),
]
});

[ 10 ] The code below creates the table of employees, iterating over the list of Employees passed as a parameter:

 let tableRows = [];
tableRows.push(tableHeaderRow);

for (const person of employeeList) {
const firstName = person.get("FirstName");
const lastName = person.get("LastName");
const gender = person.get("Gender");
const jobTitle = person.get("JobTitle");
const age = person.get("Age").toString();
const firstNameTableCell = new TableCell({ children: [new Paragraph(firstName)], });
const lastNameTableCell = new TableCell({ children: [new Paragraph(lastName)], });
const genderTableCell = new TableCell({ children: [new Paragraph(gender)], });
const jobTitleTableCell = new TableCell({ children: [new Paragraph(jobTitle)], });
const ageTableCell = new TableCell({ children: [new Paragraph(age)], });

const tableRow = new TableRow({
children: [firstNameTableCell, lastNameTableCell, genderTableCell, jobTitleTableCell, ageTableCell],
});

tableRows.push(tableRow);
}
const table = new Table({ rows: tableRows, alignment: AlignmentType.CENTER });

[ 11 ] The code below creates an MS Word Document Object (provided by the DOCX package), defines its margins, creates two paragraphs (one for the heading and another for the description), and finally saves the DOCX with the Employee data:

 const doc = new Document({
sections: [
{
properties: {
page: {
margin: {
top: '2.5cm',
right: '2.0cm',
bottom: '2.0cm',
left: '2.5cm'
},
},
},
children: [
new Paragraph({
text: "List of registered Employees",
heading: HeadingLevel.HEADING_1,
alignment: AlignmentType.CENTER,
}),
new Paragraph({
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum id velit sed elit congue imperdiet. Cras bibendum risus cursus purus interdum ultricies. Fusce sed eleifend turpis, sed dignissim ex. Maecenas eu tellus felis. Nullam luctus, magna pulvinar interdum euismod, nisl augue porta sapien, sit amet vulputate ante metus non justo. Mauris erat diam, rhoncus id congue eu, consequat vel massa. Fusce at interdum felis. Aenean id commodo ex, non semper odio. Nunc id odio ex. Donec at sapien lobortis, cursus ante et, pulvinar eros.",
alignment: AlignmentType.JUSTIFIED,
}),
table,
],
},
],
});

saveDocumentToFile(doc, "EmployeeList.docx");

[ 12 ] Now, let’s create a Nanoflow that will retrieve the Employee data and send this data to the JavaScript Action:

[ 13 ] Finally, let’s call this Nanoflow from the Overview Employee page (through the button Export to DOCX):

[ 14 ] Hit F5 to run the application locally. The result should be as follows:

[ 15 ] Click on the Export to Docx button and the DOCX document will be downloaded for you!

[ 16 ] Yes, I admit that the result isn’t that pretty. However, since this article is becoming too large, I’ll write another showing how to format the document using the DOCX API, as well as using Document Templates as a basis to generate the document.

Source Code

Conclusion & Comments

  • Mendix is a low-code technology. But low-code doesn’t mean never-code! By combining both programming approaches, we can get speedy results on the shoulders of the so-called traditional code (using Java or JavaScript).
  • There are plenty of Javascript libraries on the internet that we can take advantage of. With a little effort, we can use these solutions in a Mendix-Made application. For example:
    - Faker.JS: to generate fake data
    - Sheet.JS: to generate XLSX documents
    - Document-template: Template-based DOCX report creation for both Node and the browser.
  • All of the above-mentioned libraries will be subject of future articles here on Medium. Stay tuned!

--

--

Vinicius Strugata Ambrosio
Vinicius Strugata Ambrosio

Written by Vinicius Strugata Ambrosio

Mendix Developer, Python Enthusiast and Flutter/Dart Learner — https://www.linkedin.com/in/vstram/

No responses yet