mkdir pdfGenerator && cd pdfGenerator
create-react-app client
cd client && npm i -S axios file-saver
server
folder in the project directory and navigate to it. In it we will create the index.js
file and run the project initialization: mkdir server && cd server && touch index.js && npm init
package.json
, just press Enter
several times. After that, execute the following command to add necessary dependencies to the server part of the project: npm i -S express body-parser cors html-pdf
client/package.json
file, above the dependencies section, add the following: "proxy": "http://localhost:5000/"
client
directory and execute the following command: npm start
server
folder and execute the following command: nodemon index.js
src/App.js
client part of the application, import into the dependency code: import axios from 'axios'; import { saveAs } from 'file-saver';
state = { name: 'Adrian', receiptId: 0, price1: 0, price2: 0, }
create-react-app
and returned from the render()
method. Insert there the following: <div className="App"> <input type="text" placeholder="Name" name="name" onChange {this.handleChange}/> <input type="number" placeholder="Receipt ID" name="receiptId" onChange={this.handleChange}/> <input type="number" placeholder="Price 1" name="price1" onChange={this.handleChange}/> <input type="number" placeholder="Price 2" name="price2" onChange={this.handleChange}/> <button onClick={this.createAndDownloadPdf}>Download PDF</button></div>
handleChange
method that will be responsible for updating the application state data related to the input fields: handleChange = ({ target: { value, name }}) => this.setState({ [name]: value })
createAndDownloadPdf = () => { axios.post('/create-pdf', this.state) }
index.js
file: const express = require('express'); const bodyParser = require('body-parser'); const pdf = require('html-pdf'); const cors = require('cors');
const app = express(); const port = process.env.PORT || 5000;
req.body
. We also configure CORS so that our work would not be hindered by a Cross-Origin Request Blocked
error: app.use(cors()); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json());
app.listen(port, () => console.log(`Listening on port ${port}`));
documents
directory in the server
folder, navigate to it and create the index.js
file in it: mkdir documents && cd documents && touch index.js
module.exports = ({ name, price1, price2, receiptId }) => { ... }
index.js
from the server/documents
folder to the following form: module.exports = ({ name, price1, price2, receiptId }) => { const today = new Date(); return ` <!doctype html> <html> <head> <meta charset="utf-8"> <title>PDF Result Template</title> <style> .invoice-box { max-width: 800px; margin: auto; padding: 30px; border: 1px solid #eee; box-shadow: 0 0 10px rgba(0, 0, 0, .15); font-size: 16px; line-height: 24px; font-family: 'Helvetica Neue', 'Helvetica', color: #555; } .margin-top { margin-top: 50px; } .justify-center { text-align: center; } .invoice-box table { width: 100%; line-height: inherit; text-align: left; } .invoice-box table td { padding: 5px; vertical-align: top; } .invoice-box table tr td:nth-child(2) { text-align: right; } .invoice-box table tr.top table td { padding-bottom: 20px; } .invoice-box table tr.top table td.title { font-size: 45px; line-height: 45px; color: #333; } .invoice-box table tr.information table td { padding-bottom: 40px; } .invoice-box table tr.heading td { background: #eee; border-bottom: 1px solid #ddd; font-weight: bold; } .invoice-box table tr.details td { padding-bottom: 20px; } .invoice-box table tr.item td { border-bottom: 1px solid #eee; } .invoice-box table tr.item.last td { border-bottom: none; } .invoice-box table tr.total td:nth-child(2) { border-top: 2px solid #eee; font-weight: bold; } @media only screen and (max-width: 600px) { .invoice-box table tr.top table td { width: 100%; display: block; text-align: center; } .invoice-box table tr.information table td { width: 100%; display: block; text-align: center; } } </style> </head> <body> <div class="invoice-box"> <table cellpadding="0" cellspacing="0"> <tr class="top"> <td colspan="2"> <table> <tr> <td class="title"><img src="https://i2.wp.com/cleverlogos.co/wp-content/uploads/2018/05/reciepthound_1.jpg?fit=800%2C600&ssl=1" style="width:100%; max-width:156px;"></td> <td> Datum: ${`${today.getDate()}. ${today.getMonth() + 1}. ${today.getFullYear()}.`} </td> </tr> </table> </td> </tr> <tr class="information"> <td colspan="2"> <table> <tr> <td> Customer name: ${name} </td> <td> Receipt number: ${receiptId} </td> </tr> </table> </td> </tr> <tr class="heading"> <td>Bought items:</td> <td>Price</td> </tr> <tr class="item"> <td>First item:</td> <td>${price1}$</td> </tr> <tr class="item"> <td>Second item:</td> <td>${price2}$</td> </tr> </table> <br /> <h1 class="justify-center">Total price: ${parseInt(price1) + parseInt(price2)}$</h1> </div> </body> </html> `; };
server/index.js
include this file in the server/index.js
: const pdfTemplate = require('./documents');
create-pdf
POST route, we will use the pdf.create()
command, referring to the object imported from the html-pdf
module.pdf.create()
method, an HTML template is used, as well as data from the client.pdf.create()
, we will call the toFile()
method, passing it the name we want to assign to the PDF document, as well as the arrow callback function. This function, if an error occurs, will execute the res.send(Promise.reject())
command. In case everything went well, it will execute the res.send(Promise.resolve())
command res.send(Promise.resolve())
. app.post('/create-pdf', (req, res) => { pdf.create(pdfTemplate(req.body), {}).toFile('result.pdf', (err) => { if(err) { res.send(Promise.reject()); } res.send(Promise.resolve()); }); });
res.sendFile()
: app.get('/fetch-pdf', (req, res) => { res.sendFile(`${__dirname}/result.pdf`) })
createAndDownloadPdf
function. Here we perform a POST request to the server using the axios
module. Now this function looks like this: createAndDownloadPdf = () => { axios.post('/create-pdf', this.state) }
axios.post()
. then()
. We are allowed to do this by responding to a client's POST request, we return a promise from the server, which can either be successfully resolved or rejected. .then(() => axios.get('/fetch-pdf', { responseType: 'blob' })) .then(( res) => {})
blob
used as responseType
. Before we move on - let's talk about what it is.Blob
object stores a link to a file, but in fact it is not. These objects store data that you can work with. For example - they can be saved to files.Blob
objects are, we can use another .then()
call to res.data
new Blob
object based on res.data. When creating this object, we will pass to its constructor a parameter indicating that the data that will store the object is of type application/pdf
. After that, we can use the saveAs()
method that we imported from the file-saver
module, and save the data to a file. As a result, the code of the createAndDowndloadPdf
method will look as shown below: createAndDownloadPdf = () => { axios.post('/create-pdf', this.state) .then(() => axios.get('fetch-pdf', { responseType: 'blob' })) .then((res) => { const pdfBlob = new Blob([res.data], { type: 'application/pdf' }); saveAs(pdfBlob, 'newPdf.pdf'); }) }
Download PDF
button, data is transferred to the server and a PDF document is downloaded from it.Source: https://habr.com/ru/post/460018/
All Articles