Recently I've been working in a web based aplication (front-end using Vue.js) which final result is a detailed report. Everything was great, but when I share the final result with the owners of the aplication they thought that it would be awesome if they were able to download all this information into a .docx document with some kind of format. I had never done anything like this before, but I thought it should not be so difficult. So I started digging into the web looking for some javascript tool that could help me solve this requirement, so I found docx, a great tool to easily generate .docx files with JS/TS with a solid API.
You are able to use docx with any Javascript library (React.js, Angular, Vue.js), but this time I want to share my experience using it with Vue.js so if someone is in the same situation that I was can use this post as reference. Without more to say, let's start!
First you have to install docx and FileSaver.js (solution to saving files on the client-side) in your component. I used npm but you can use yarn or whatever you want.
npm install --save docx file-saver
Then import the packages to your component like this:
<script>
import { WidthType, BorderStyle, Document, Paragraph, Packer, TextRun }
from "docx";
import { saveAs } from 'file-saver';
export default {
components: {
Document, Paragraph, Packer, TextRun, saveAs, BorderStyle, WidthType
},
data: () => ({
}),
methods: {
},
created(){
}
}
</script>
After that, you create a method so when the user click a button the .docx file is automatically generated.
<template>
<div class="btn btn-link float-right" @click="exportDocx">
<i class="far fa-file-word"></i>
Generate .docx file
</div>
</template>
<script>
import { WidthType, BorderStyle, Document, Paragraph, Packer, TextRun }
from "docx";
import { saveAs } from 'file-saver';
export default {
components: {
Document, Paragraph, Packer, TextRun, saveAs, BorderStyle, WidthType
},
data: () => ({
state: {
name: 'San Luis Potosi'
}
}),
methods: {
// Create a new Document an save it in a variable
let doc = new Document();
// Add paragraph in the document
let title = new Paragraph(`Detailed Report for ${this.state.name}`).title().center();
// To export into a .docx file
let packer = new Packer();
packer.toBlob(doc).then(blob => {
saveAs(blob, "detailed_report.docx");
// using sweet alert for notification
toast({
type: 'success',
title: 'Document created!'
})
});
},
created(){
}
}
</script>
docx allows you to add text, images, tables, bullet points, numbering and more...
In my case I used text for titles, headings and content; added a base64 image; bullet points to organize data and tables, so I'll give you an example of how to create a document with this resources and if you need something a little more complex you can always check the documentation for more info.
<template>
<div class="btn btn-link float-right" @click="exportDocx">
<i class="far fa-file-word"></i>
Generate .docx file
</div>
</template>
<script>
import { WidthType, BorderStyle, Document, Paragraph, Packer, TextRun }
from "docx";
import { saveAs } from 'file-saver';
export default {
components: {
Document, Paragraph, Packer, TextRun, saveAs, BorderStyle, WidthType
},
data: () => ({
state: {
name: 'San Luis Potosi',
map: 'data:image/png;base64',
municipalities: [
{name:'San Luis Potosi', population: 824000},
{name:'Rio Verde', population: 160000},
{name:'Cd Valles', population: 176000},
{name:'Matehuala', population:82726}
],
tourist_attractions: [
'Tamtoc', 'Sótano de las Golondrinas', 'Cascada de Tamul'
]
}
}),
methods: {
// Create a new Document an save it in a variable
let doc = new Document();
// Add paragraph in the document
doc.addParagraph(new Paragraph(`Detailed Report for ${this.state.name}`).title().center());
// Add heading for map
doc.addParagraph(new Paragraph(`State Map`).heading1().thematicBreak().center());
// Add map image
doc.createImage(this.state.map, 600, 250, {});
// Add heading for attractions
doc.addParagraph(new Paragraph(`Tourist Attractions`).heading1().thematicBreak().center());
// Bullet points
for (let attraction of this.state.tourist_attractions) {
doc.addParagraph(new Paragraph(attraction).bullet());
}
// Add heading for municipalities
doc.addParagraph(new Paragraph(`Municipalities`).heading1().thematicBreak().center());
// Create table
let municipalities_table = doc.createTable({
rows: this.state.municipalities.length+1,
columns: 2,
width: 100,
widthUnitType: WidthType.AUTO,
columnWidths: [2934, 2934],
});
municipalities_table.getCell(0, 0).addParagraph(new Paragraph("Name"));
municipalities_table.getCell(0, 1).addParagraph(new Paragraph("Population"));
for (let [index, municipality] of this.state.municipalities.entries()) {
municipalities_table.getCell(index+1, 0).addParagraph(new Paragraph(municipality.name));
municipalities_table.getCell(index+1, 1).addParagraph(new Paragraph(municipality.population));
}
// To export into a .docx file
let packer = new Packer();
packer.toBlob(doc).then(blob => {
saveAs(blob, "detailed_report.docx");
// using sweet alert for notification
toast({
type: 'success',
title: 'Document created!'
})
});
},
created(){
}
}
</script>
And that's it, a simple yet powerful component to create .docx files when a user clicks a button.
Really hope this post is usefull, if so, please share and tell me if you know another way to do this, till the next post.
Top comments (3)
I'm working on getting something similar working. As it stands, the post you published above does not have working code. Do you have a codepen or link to github with working code? Are you able to run the code above successfully?
The project where I use this code is private, but I'll take some time to create a pen and update this post. Greetings
Hi, Excelent post, I use it and work fine!
I have an extra question I cant found in the documentation.
There is a way to create legal size docx with this dependencie?
Thanks!