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.