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-script

    More 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 my result 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/scripts

    function 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);
    }
    

Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.