Skip to main content

Lightning: Generate PDF from Lightning components with in-memory data

I'm sure as everyone is diving into lightning components development, they are getting acquainted with the nuances of the Lightning components framework. As well as, its current limitations. Being a new framework, this is bound to happen. Although we have our users still using salesforce classic, we have started using lightning components framework our primary development platform and Visualforce is considered primarily for rendering lightning components within Classic Service console.

Recently, while re-architecting a critical module, we encountered a problem wherein we needed to generate PDF from lightning components. Now, being Javascript intensive framework, it has limited room for such features (may be included in future roadmap). As of now, there is no native feature within the lightning framework to do so (at least I didn't find anything).

Common Scenario - Create Visualforce page to retrieve data and generate PDF

For scenarios where the data exist within Salesforce, it may be a comparatively easier task, by developing a visualforce page, which can accept arguments in querystring and generate PDF by retrieving data via Apex controller. Further, Lightning component can generate desired URL to retrieve the desired PDF and open that URL in a new window.

Complex Scenario - Generate PDF from Lightning components with in-memory data

However, in our scenario, all the data is being retrieved from an external application at run-time. This data is never saved within Salesforce and hence we have to make use of in-memory data. However, we do have this feature available within Visualforce. As we are still using Salesforce classic and embedding our lightning components on visualforce page, we have an advantage in this case.

So, here is a quick list of challenges and how I solved them in this solution (not saying that there could not be any better solutions)

Challenge Solution
Generate PDF Generate PDF via Visualforce
Send Data to PDF page Use hidden fields or method arguments to pass data from Lightning components to Visualforce page controller

Process flow

Process flow - Generate PDF from Lightning components with in-memory data


Steps

So, I utilized Visualforce PDF generation feature triggered from Lightning component. The main steps in this task are:-
  1. Create Lightning component to accept an external method and invoke it on button click
  2. Embed Lightning component within Visualforce Page
  3. Passing in-memory data within lightning component to VF Page Controller
  4. Generating PDF

Create Lightning component to accept an external method and invoke it on button click

In this step, we will create a lightning component which accepts a javascript method as an attribute and on button click (of button within lightning component), invokes visualforce page's javascript method

We can create an attribute within Lightning component to accept a function. For demo purposes, I've added a button, which will invoke PDF generation

1
2
3
4
5
6
7
8
<aura:component access="global">

    <!-- attribute to accept Visualforce page's javascript method -->
    <aura:attribute name="sendData" type="object"/>

    <!-- Button component to invoke PDF download -->
    <lightning:button label="Download Document" onclick="{!c.downloadDocument}" />
</aura:component>


We can create lightning component's controller to invoke VF Page javscript method on button click

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* Component controller */
({
 downloadDocument : function(component, event, helper){

  var sendDataProc = component.get("v.sendData");
  var dataToSend = {
   "label" : "This is test"
  }; //this is data you want to send for PDF generation

  //invoke vf page js method
  sendDataProc(dataToSend, function(){
              //handle callback
  });
 }
})

Embed Lightning component within Visualforce Page

Here, we will embed the newly created lightning component to our VF page MainPage. We can pass a javascript function (defined within v the sualforce page) to lightning component upon initialization.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function saveData(data, callback){
    //TO DO
}

$Lightning.use("DemoApp", function() {
    $Lightning.createComponent("PDFGenertorExample",
    { 
        sendData : saveData
    },
    "lightning",
    function(cmp) {
        // do some stuff
    });
});

Passing in-memory data within lightning component to VF Page Controller

The way I have achieved it is by using a hidden control to store the data and bind it with controller property.


1
2
3
4
/* Controller code */
public class VFPageController {
    public String PDFData {get; set;}    
}

Second, embed an apex:inputhidden component to bind this property to page
1
2
<!-- Page code -->
<apex:inputhidden id="hidData" value="{!PDFData}"/>


Thirdly, bind the data received from component to page's control

1
2
3
4
5
6
<!-- Page JS code -->
function saveData(data, callback){
    
    var hidData = document.getElementById('{!$Component.hidData}')
    hidData.value = JSON.stringify(data);
}


Generate PDF

So far, we have successfully brought lightning component's in-memory data to Visualforce page control, where the page control is further bound to Apex controller's property.

Now, the last part is to actually invoke the PDF generation logic. Now, we need to create a visualforce page to generate the actual PDF and invoke this page.

So, for the sake of simplicity, we create a visualforce page PDFPage as


1
2
3
4
<!-- PDFPage -->
<apex:page controller="VFPageController" renderAs="pdf">
    {!PDFData}
</apex:page>


Now, we need to create an apex controller method in VFPageController class to open the PDF page (to trigger generation and download of PDF document).



1
2
3
4
5
6
7
8
9
<!-- generate and download pdf document -->
public PageReference downloadPDF(){
    System.PageReference pageRef = new System.PageReference('/apex/PDFPage');

    //ensure pdf downloads and is assigned with defined name
    pageRef.getHeaders().put('content-disposition', 'attachment; filename=TestPDF.pdf');
 
    return pageRef;
}


Lastly, we need to add an actionfunction to MainPage, to invoke controller method; and invoke this actionfunction method on receiving invocation from Lightning component

So, our actionfunction code will be:


<apex:actionfunction name="jsGeneratePDF" action="{!downloadPDF}" />
and, our updated saveData method will be:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function saveData(data, callback){
    //bind lightning component data to page component
    var hidData = document.getElementById('{!$Component.hidData}')
    hidData.value = JSON.stringify(data);

    //invoke PDF Generation
    jsGeneratePDF();
 
    //invoke callback;
    if(typeof callback == 'function') callback();
}



Update(Mar 15, 2018): I’ve uploaded sample code to Github Gist(finally!!). Following is the link

https://gist.github.com/toanshulverma/16244d19ac68364cb75443695d81403b

Comments

  1. i want to have button on my web page and when we click the button it should create PDF same as my web page's output

    ReplyDelete

Post a Comment

Popular posts from this blog

Quick Tips: Salesforce default Images

Well, I'm sure a lot of you still rely on using out of the box salesforce images for displaying quick icons within formula fields or even using them within your Visualforce pages. Lately, I realized that a lot of earlier resources are no longer accessible, so I tried to quickly extract all images from Salesforce CSS files and provide a quick reference here. Please note, I've referenced all images from SF servers directly, so if anything changes, the image should stop rendering here. As these images are completely controlled by Salesforce, and in case they change anything, it might lead to image not being accessible. Image path Image /img/samples/flag_green.gif /img/samples/flag_green.gif /img/samples/flag_red.gif /img/samples/color_red.gif /img/samples/color_yellow.gif /img/samples/color_green.gif /img/samples/light_green.gif /img/samples/light_yellow.gif /img/samples/light_red.gif /img/samples/stars_100.gif /img/samples/stars_200.gif /img/samples/stars_300.

Quick Tips: Setup SFDX Manually without Admin access

We all have faced challenges while working in different enterprise environments, where there may be lot of controls/ checks/ red-tape to get by. In such situations, getting access to simple tools (even git) can take lot of time. Note: This tutorial is to be followed at your own risk, as it may not be complaint to your organization's IT policies. What is SFDX? SFDX is a command line utility for managing salesforce builds/ deployments. Being command line, it can be easily embedded to automation chains, to help build fully automated build and deployment processes. To get started, refer  https://trailhead.salesforce.com/en/content/learn/trails/sfdx_get_started Setup SFDX on Windows machine without admin access As you may have already realized, SFDX installation needs admin access to one's machine. Which may be a luxury a lot of developers may not have. So, i tried to provide a step-by-step guide to setup SFDX on your computer without any admin access Steps: Note: