Schedule Render multiple PDFs using same template but pass different data using API
-
Hi is it possible to have an API request which passes through json data and for each record render a PDF template and use the scheduler to send the rendered template.
So far the jsreport tutorial has been a lot of help but what I am looking to do is use the beforeRender function to use the API data and render multiple reports.
For example an API passes through 3 records of data, we loop through each record. The first record renders a template using the data and then send an email to the recipients. The second then does the same and so does the third.
I want it all to be dynamic using the API rather then having to create a seperate schedule per record. We have close to 100 hundred records which will be passed in every month and I'd prefer not to have to create 100 different schedules.
-
You can use
beforeRender
as you mentioned and dynamically invoke rendering of other templates and do with the result whatever you want.
The documentation
https://jsreport.net/learn/scripts#rendering-another-template-from-scriptMore specifically to your case, it would be something like
const jsreport = require('jsreport-proxy') const amailer = require('<amailer>') async function beforeRender(req, res) { for (let item of req.data.myrecord) { const result = await jsreport.render({ template: { shortid: 'xxxxxx' }, data: item }) await amailer({ to: 'foo', attachment: result.content }) } }
-
Hi sorry it has taken me so long to reply, I have been ill over the past few days.
I have added the following code:
const jsreport = require('jsreport-proxy') async function beforeRender(req, res) { var templates = ['H1xtsYmyWI', 'SJgs0QdlzU']; for (let template of templates) { const result = await jsreport.render({ template: { shortid: template }}) var nodemailer = require("nodemailer"); var sesTransport = require('nodemailer-ses-transport'); var transporter = nodemailer.createTransport(sesTransport({ accessKeyId: "", secretAccessKey: "", region: 'eu-west-1', rateLimit: 5 })); var email_address = ["email1", "email2"]; email_address.forEach(function(email) { var mail = { from: "email", to: email, subject: "Sending email from node.js", text: "See the attached report", html: "<b>See the attached report</b>", attachments: [ { filename: 'Report.pdf', content: new Buffer(result.content) }], } transporter.sendMail(mail, function(error, response){ transporter.close(); if(error){ return done(error); } return done(); }); }); } }
but keep receiving the following error, im not sure my code is correct in all honesty
Error: Render cycle detected. Template at /Intelli-Sense-Monthly-Report/template was rendered previously in this render request (hierarchy: /Intelli-Sense-Monthly-Report/template2 -> /Intelli-Sense-Monthly-Report/template -> /Intelli-Sense-Monthly-Report/template). Please verify that reporter.render is not causing cycle at Object.callbackRequests.(anonymous function) (/var/www/html/jsreportapp/node_modules/script-manager/lib/worker-servers.js:250:31) at process.<anonymous> (/var/www/html/jsreportapp/node_modules/script-manager/lib/worker-servers.js:195:30) at process.emit (events.js:203:15) at process.EventEmitter.emit (domain.js:448:20) at emit (internal/child_process.js:832:12) at process._tickCallback (internal/process/next_tick.js:63:19)
-
The error seems self-explaining.
Doesn't, for example, one of these temples have the posted script linked to itself?
var templates = ['H1xtsYmyWI', 'SJgs0QdlzU'];
-
This post is deleted!
-
Thanks for the reply, what i would love to do is only use one template so instead of my example where I looped over the template shortcodes I have
var venues = [{'name': 'Hinckley'}, {'name': 'Stockport'}];
and loop over that and then myresult
const is as follows -const result = await jsreport.render({ template: { shortid: 'H1xtsYmyWI', data: venue }})
Is that possible? the reason i want it to work like this is because i will pass in API data which contains different data per venue and then I load this data into the template per venue and send it to the relevant people. hopefully that makes sense, sorry for any confusion.
I am presuming if I do it this way i need to keep the script linked to my template but then it obviously throws the previously mentioned error.
-
I probably don't get it fully.
However, I have a feeling it could help you to exit the script when it is running for the nested template.
This can be done like this.
https://jsreport.net/learn/scriptsfunction beforeRender(req, res) { //filter out script execution for chrome header if (req.context.isChildRequest) { return } //your script code }
-
Sorry I may not have explained it correctly, i will show you all my code which will hopefully make more sense.
const jsreport = require('jsreport-proxy') async function beforeRender(req, res) { var venues = [ {'name': 'Hinckley', 'data': 10}, {'name': 'Stockport', 'data': 10}, {'name': 'Hereford', 'data': 10}, {'name': 'Norwich', 'data': 10} ]; for (let venue of venues) { console.log(venue); const result = await jsreport.render({ template: { shortid: 'H1xtsYmyWI', data: venue }}) var nodemailer = require("nodemailer"); var sesTransport = require('nodemailer-ses-transport'); var transporter = nodemailer.createTransport(sesTransport({ accessKeyId: "", secretAccessKey: "", region: 'eu-west-1', rateLimit: 5 })); var email_address = ["email1"]; email_address.forEach(function(email) { var mail = { from: "email2", to: email, subject: "Sending email from node.js", text: "See the attached report", html: "<b>See the attached report</b>", attachments: [ { filename: 'Report.pdf', content: result.content }], } transporter.sendMail(mail, function(error, response){ transporter.close(); if(error){ return done(error); } return done(); }); }); } }
As you can see I have a list of venues, each venue will use the same template but will send different data that the template will display. So in this example I have 4 venues, I need to render the
H1xtsYmyWI
template 4 times and after each one is rendered send it in an email.is something like this possible? in my head the code example shoudl work but I could be completely wrong.
Thank you for the help so far btw!
-
I have now changed my code a bit, you can see below -
const jsreport = require('jsreport-proxy') async function beforeRender(req, res) { if (req.context.isChildRequest) { return } var venues = [ {'name': 'Hinckley', 'data': 10}, {'name': 'Stockport', 'data': 10} ]; var nodemailer = require("nodemailer"); var sesTransport = require('nodemailer-ses-transport'); var transporter = nodemailer.createTransport(sesTransport({ accessKeyId: "", secretAccessKey: "", region: 'eu-west-1', rateLimit: 5 })); for (let venue of venues) { const result = await jsreport.render({ template: { shortid: 'H1xtsYmyWI', data: { post : venue } } }); await sendEmail(transporter, result); } } function sendEmail(transporter, result) { var email_address = [email1]; email_address.forEach(function(email) { var mail = { from: "email2", to: email, subject: "Sending email from node.js", text: "See the attached report", html: "<b>See the attached report</b>", attachments: [ { filename: 'Report.pdf', content: result.content }], } transporter.sendMail(mail, function(error, response){ transporter.close(); if(error){ return done(error); } }); }); }
I also commented out the code change made here - https://github.com/jsreport/jsreport-templates/commit/4e86708087bd07e771a06d5a213e00923be77969#diff-fc7a5842f485586c9089b6c1d162304d
I need to load the same template multiple times and if I am correct this was preventing me from doing so. The issue I have now is the data is not being passed to the template, the attachments on the emails i receive don't contain any of the data i passed through.
I am trying to access the data in the template using the following
{{post.name}}
. there may be an underlying issue which is causing this but hopefully i have just made a stupid mistake.
-
Sorry for all the posts but i managed to solve everything, the data wasn't being passed to the template because I made a silly mistake of setting data within the template object instead of doing the following.
for (let venue of venues) { const result = await jsreport.render({ template: { shortid: 'H1xtsYmyWI' }, data: { post : venue } }); await sendEmail(transporter, result); }