Send LWC as PDF attachment in an Email


Use Case: We have an requirement where we need to send chart rendered on LWC component as PDF attachment in an email.

Solution: We have external libraries available which can help to build the above use case.

Prerequisite:

Download below static resource file from below.

1. Jquery.min.Js
2. JSPdf.js
3. html2canvas.min.js
4. rgbcolor.js
5. Canvg,js


This post is an enhancement on my previous blog to send LWC component as a PDF attachment.

Creating the component

displayChart.html

<template> 
    <div class="chart" lwc:dom="manual"></div>
    <div class = 'slds-box slds-theme_default printPdf' > <!-- content to be printed inside the pdf included in div-->
        <canvas class="pie-chart"  width="800" height="450" lwc:dom="manual"></canvas>
    </div>
    <div class="slds-box slds-theme_default">
            <lightning-button label="Send Email" onclick={printPdf}></lightning-button> <!-- call the method to send lwc content as pdf-->
    </div>
</template>



displayChart.js

import { LightningElement,api,wire,track  } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import ChartJS from '@salesforce/resourceUrl/ChartJS';
import JSPdf from '@salesforce/resourceUrl/JSPdf';
import HTMLCanvas from '@salesforce/resourceUrl/HTMLCanvas';
import RGBColor from '@salesforce/resourceUrl/RGBColor';
import CanvgJS from '@salesforce/resourceUrl/CanvgJS';
import JqueryminJS from '@salesforce/resourceUrl/JqueryminJS';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import getLeadByStatus from '@salesforce/apex/LeadGraphController.getLeadByStatus';
import sendEmail from '@salesforce/apex/LeadGraphController.sendEmail';

export default class DisplayChart extends LightningElement {
    @track dataSet;
    @wire(getLeadByStatus)wiredLeads(result){
        if(result.data){
            this.dataSet = result.data; 

            console.log(result.data);
            this.Initializechartjs();
        } else if (result.error){

        }
    }



    @api chartjsInitialized = false;
    renderedCallback() {
        if (this.chartjsInitialized) {
          return;
         }
        this.chartjsInitialized = true;

        Promise.all([
              loadScript(this, ChartJS),
              loadScript(this,JqueryminJS),
              loadScript(this, JSPdf),
              loadScript(this, HTMLCanvas),
              loadScript(this, RGBColor),
              loadScript(this, CanvgJS)

        ])
    .then(() => {
        //this.Initializechartjs();
    })
    .catch(error => {


    });
   }

   Initializechartjs() {
        console.log("loaded");
        var piechart;
        var ctx = this.template.querySelector(".pie-chart").getContext('2d');

        piechart = new Chart(ctx ,{
            type: 'pie',
            data: {
                labels: Object.keys(this.dataSet),
                datasets: [
                    {
                        label:'count',
                        data: Object.values(this.dataSet),
                        backgroundColor: ["#0074D9", "#FF4136", "#2ECC40", "#FF851B", "#7FDBFF", "#B10DC9", "#FFDC00", "#001f3f", "#39CCCC", "#01FF70", "#85144b", "#F012BE", "#3D9970", "#111111", "#AAAAAA"]

                    }
                ]
            }
        });
    }

    //To generate a blob body of Div into a pdf using JSPdf library.
    printPdf() {
        var nodesToRecover = []
        var targetElem = this.template.querySelector(".printPdf"); // Select the element which needs to printed in pdf format
        var nodesToRemove = []
        var svgs = $(targetElem).find('svg');
        svgs.each(function(index, node) {
            var parentNode = node.parentNode
            var svg = parentNode.innerHTML
            var canvas = document.createElement('canvas')
            xml = (new XMLSerializer()).serializeToString(node)
            xml = xml.replace(/xmlns=\"http:\/\/www\.w3\.org\/2000\/svg\"/, '')
            canvg(canvas, xml); // html to image
            nodesToRecover.push({
                parent: parentNode,
                child: node
             })
            parentNode.removeChild(node)
            nodesToRemove.push({
            parent: parentNode,
                child: canvas
             })
            parentNode.appendChild(canvas)
        })
        html2canvas(targetElem, {
            onrendered: function(canvas) {
            canvas.style.visibility = 'hidden'
            document.body.appendChild(canvas);
            var doc = new jsPDF('p', 'pt', [canvas.height,canvas.width]); // create pdf file
            doc.addHTML(canvas, {}, function() { // add image to pdf file
                 
                console.log(btoa(doc.output()));
                sendEmail({pdfBody: btoa(doc.output())})
                    .then(result => {

                    })
                    .catch(error => {
                        console.log(error);
                    });
                    document.body.removeChild(canvas)

                })

            }

        })


    }

}


LeadGraphController.cls

public with sharing class LeadGraphController {

    @AuraEnabled(cacheable=true)

    public static Map<String, Decimal> getLeadByStatus() {
        List<AggregateResult> result =  [Select Count(Id) leadCount, Status st from Lead
                                             GROUP BY Status];
        Map<String, Decimal> wrapp = new Map<String, Decimal>();
        for(AggregateResult ar:result){
            wrapp.put(String.valueOf(ar.get('st')),(Decimal)ar.get('leadCount'));

        }
        return wrapp;
    }
    @AuraEnabled
    public static string sendEmail(String pdfBody){
        Messaging.EmailFileAttachment attach = new Messaging.EmailFileAttachment();
        attach.setContentType('application/pdf');
        attach.setFileName('Report.pdf');
        attach.setInline(false);
        attach.Body = EncodingUtil.base64Decode(pdfBody);

        list<Messaging.singleEmailMessage> mails=new list<Messaging.SingleEmailMessage>();
        Messaging.singleEmailMessage mail=new Messaging.SingleEmailMessage();
        list<String> toadd=new List<String>{'mporwal92@gmail.com'};

        mail.setFileAttachments(new Messaging.EmailFileAttachment[] { attach });
        mail.setToAddresses(toadd);
        mail.setSubject('Pdf Report');
        mail.setPlainTextBody('Chart as PDF Attachment');
        mails.add(mail);
        messaging.sendEmail(mails);
        return null;
    }
}


Do not hesitate to give me feedback!!!!! :)












Comments

  1. I am getting this error.

    jspdf.debug.js:12989 Uncaught (in promise) TypeError: Cannot read property 'open' of undefined
    at eval (jspdf.debug.js:12989)
    at new Promise ()
    at module.exports (jspdf.debug.js:12965)
    at renderDocument (jspdf.debug.js:13343)
    at Proxy.html2canvas (jspdf.debug.js:13316)
    at y.printPdf (dealFastTrackRequestReview.js:4)
    at callHook (aura_prod.js:29)
    at aura_prod.js:4
    at Ir (aura_prod.js:4)
    at go (aura_prod.js:4)

    ReplyDelete
  2. Did you copy the JS file and loaded them separately in Static Resource. Also could you share the code snippet to my email id mporwal92@gmail.com and JS file. Also you can drop your email id and will share you the files.

    ReplyDelete
  3. Did you copy the JS file and loaded them separately in Static Resource. Also could you share the code snippet to my email id mporwal92@gmail.com and JS file. Also you can drop your email id and will share you the files.

    ReplyDelete
  4. I have used the above sample to try and save a PDF from a LWC component but we can not create the jsPDF object (ie var doc = new jsPDF(); // create pdf file). There seems to be some confusion if jsPDF is supported in LWC. We can get the jsPDF to load with loadScript but then nothing.

    ReplyDelete
    Replies
    1. JSPDF is supported in LWC component. I have a working example in my org. Let me know if we can connect.

      Delete
  5. Hi,
    I am getting an error "JSPDF:29 jsPDF PubSub Error Right-hand side of 'instanceof' is not an object TypeError: Right-hand side of 'instanceof' is not an object". Let me know if this is particular to version of the jspdf.

    Thanks,
    Sakshi Sharma

    ReplyDelete
    Replies
    1. Hi Sakshi,

      I will share the working example of the component and static resource file. Please provide your gmail id

      Delete
    2. Hi Manish,
      Thanks a lot. But it worked for me. though I have one question. I also want to download the pdf Document when clicked on the button. I tried doc.save('docname.pdf'). But its not working.
      Let me know if you can guide for the same.

      Regards,
      Sakshi Sharma

      Delete
  6. Please let me know if you can provide the code sample

    ReplyDelete
    Replies
    1. Yes i can provide the code sample. Please send me an email to mporwal92@gmail.com

      Delete
  7. Hello Manish,
    Can u please send me the working code sample??

    ReplyDelete
  8. Hello Manish,

    I have a requirement that should convert lwc component html into pdf and download on button click.

    can u please send me the code sample?

    Thanks in Advance.

    ReplyDelete
  9. Does this work past api version 39?

    ReplyDelete
  10. I am facing an issue in canvg.js TypeError: Cannot set properties of undefined in this method
    this.canvg = function (target, s, opts) {
    // no parameters
    if (target == null && s == null && opts == null) {
    var svgTags = document.getElementsByTagName('svg');
    for (var i=0; i<svgTags.length; i++) {
    var svgTag = svgTags[i];
    var c = document.createElement('canvas');
    c.width = svgTag.clientWidth;
    c.height = svgTag.clientHeight;
    svgTag.parentNode.insertBefore(c, svgTag);
    svgTag.parentNode.removeChild(svgTag);
    var div = document.createElement('div');
    div.appendChild(svgTag);
    canvg(c, div.innerHTML);
    }
    return;
    }
    opts = opts || {};

    if (typeof target == 'string') {
    target = document.getElementById(target);
    }

    // store class on canvas
    if (target.svg != null) target.svg.stop();
    var svg = build();
    // on i.e. 8 for flash canvas, we can't assign the property so check for it
    if (!(target.childNodes.length == 1 && target.childNodes[0].nodeName == 'OBJECT')) target.svg = svg;
    svg.opts = opts;

    var ctx = target.getContext('2d');
    if (typeof(s.documentElement) != 'undefined') {
    // load from xml doc
    svg.loadXmlDoc(ctx, s);
    }
    else if (s.substr(0,1) == '<') {
    // load from xml string
    svg.loadXml(ctx, s);
    }
    else {
    // load from url
    svg.load(ctx, s);
    }
    }

    ReplyDelete
  11. The Best Baccarat Sites in Canada - FEBCASINO
    A guide to the best baccarat 바카라 사이트 sites deccasino in 온카지노 Canada. The Canadian Baccarat Rules. The rules of the game are simple.

    ReplyDelete

Post a Comment

Popular posts from this blog

Multi Select Look Up Using LWC

Draw Chart in LWC using chart.js