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!!!!! :)
I am getting this error.
ReplyDeletejspdf.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)
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.
ReplyDeleteDid 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.
ReplyDeleteI 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.
ReplyDeleteJSPDF is supported in LWC component. I have a working example in my org. Let me know if we can connect.
DeleteI will email you.
DeleteHi,
ReplyDeleteI 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
Hi Sakshi,
DeleteI will share the working example of the component and static resource file. Please provide your gmail id
Hi Manish,
DeleteThanks 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
Please let me know if you can provide the code sample
ReplyDeleteYes i can provide the code sample. Please send me an email to mporwal92@gmail.com
DeleteHello Manish,
ReplyDeleteCan u please send me the working code sample??
Hello Manish,
ReplyDeleteI 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.
Does this work past api version 39?
ReplyDelete@Unknown No its not.
ReplyDeleteI had a feeling. Thanks.
Deletewhere is Chart.js
ReplyDeleteI am facing an issue in canvg.js TypeError: Cannot set properties of undefined in this method
ReplyDeletethis.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);
}
}
The Best Baccarat Sites in Canada - FEBCASINO
ReplyDeleteA guide to the best baccarat 바카라 사이트 sites deccasino in 온카지노 Canada. The Canadian Baccarat Rules. The rules of the game are simple.