+ Start a Discussion
Bertrand DBBertrand DB 

Lightning component: save a generated PDF as File/Attachment

Hello,

I have a Lightning component on my opportunity page that does the following:
  • When clicking on the button, it opens a Lightning modal box
  • This modal box display a Visualforce page, renderas PDF, inside an iframe.
  • I have two button bellow: Cancel, Save
The purpose is to generate and save a PDF to the current record. the modal box allows to preview the document before saving attach it.

-> My issue is: how to save the PDF generated by the Visualforce page, and attach it to the Opportunity?
Any ideas?

demoModal.cmp:
<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
  <!--use boolean attribute for Store true/false value,
    make default to "false" so modal box are not display on the load of component. 
    --> 
    
    <aura:attribute name="recordId" type="Id" />

  <aura:attribute name="isOpen" type="boolean" default="false"/>
 
  <!--Use "slds-m-around- -xx-large" class to add standard Large padding to the component--> 
  <div class="slds-m-around--xx-large">
    <button class="slds-button slds-button--brand" onclick="{!c.openModel}">Create a document</button>  
    
  <!--Use aura:if tag to display Model Box, on the bese of conditions. [isOpen boolean attribute] -->   
    <aura:if isTrue="{!v.isOpen}">
      
   <!--###### MODAL BOX Start From Here ######--> 
      <div role="dialog" tabindex="-1" aria-labelledby="header99" class="slds-modal slds-fade-in-open ">
        <div class="slds-modal__container">
          <!-- ###### MODAL BOX HEADER Part Start From Here ######-->
          <div class="slds-modal__header">
            <button class="slds-button slds-modal__close slds-button--icon-inverse" title="Close" onclick="{!c.closeModel}">
            X
            <span class="slds-assistive-text">Close</span>
            </button>
            <h2 id="header99" class="slds-text-heading--medium">Your document</h2>
          </div>
          <!--###### MODAL BOX BODY Part Start From Here ######-->
            <iframe src="{! '/apex/monpdf?Id='+v.recordId}" width="100%" height="500px;" frameBorder="0"/>
          <div class="slds-modal__content slds-p-around--medium">
            <p><b>Some random text.
              </b>
            </p>
          </div>
          <!--###### MODAL BOX FOOTER Part Start From Here ######-->
          <div class="slds-modal__footer">
            <button class="slds-button slds-button--neutral" onclick="{!c.closeModel}" >Cancel</button>
            <button class="slds-button slds-button--brand" onclick="{!c.savenClose}">Save the document</button>
          </div>
        </div>
      </div>
      <div class="slds-backdrop slds-backdrop--open"></div>
      <!--###### MODAL BOX Part END Here ######-->
    
 </aura:if>
  </div>
</aura:component>

monpdf.vfp:
<apex:page standardController="Opportunity" showHeader="false" renderAs="pdf">
{!Opportunity.Account.Name}
</apex:page>

demoModalController.js
({
   openModel: function(component, event, helper) {
      // for Display Model,set the "isOpen" attribute to "true"
      component.set("v.isOpen", true);
   },
 
   closeModel: function(component, event, helper) {
      // for Hide/Close Model,set the "isOpen" attribute to "Fasle"  
      component.set("v.isOpen", false);
   },
 
   savenClose: function(component, event, helper) {
      // Display alert message on the click on the button from Model Footer 

      
       alert('It works');
      component.set("v.isOpen", false);
   },
})

 
Best Answer chosen by Bertrand DB
sachin kadian 5sachin kadian 5
i just tried this and was able to create the attachment successfully - 

I created one vf page that renders a PDF
 
<apex:page controller="TextVFPDFController" renderAs="PDF">
    {!opp.Name}
</apex:page>

I am just showing opportunity name in PDF. Here is the controller for vf page - 
 
public class TextVFPDFController {
    public Opportunity opp{get;set;}
    public TextVFPDFController(){
        Id oppId = apexpages.currentpage().getparameters().get('id');
		opp = [select id,Name from opportunity where id=: oppId];
    }
}

Now i created one lightning component  which has only one save button , on click of this button , it will create same pdf and attach it to an account 
 
public class TestAppController {
	@auraEnabled
    public static void savePDFOpportunity(){
        PageReference pdfPage = new PageReference('/apex/TextVFPDF');
		pdfPage.getParameters().put('Id', '0062800000C044o');
        Blob pdfContent = pdfPage.getContent();
        
        Attachment attach1= new Attachment();
        attach1.ParentId = '0010K00001e2kcG';
        attach1.Name = 'Test Attachment for PDF';
        attach1.Body = pdfContent;
        attach1.contentType = 'application/pdf';
		insert attach1;
        
    }
}
 
<aura:application controller="TestAppController">
	<lightning:button variant="brand" label="Save" onclick="{! c.savePDF }" />
</aura:application>
 
({
    savePDF : function(cmp, event, helper) {
        var action = cmp.get("c.savePDFOpportunity");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                alert('Attachment saved successfully');
                
            }
            else if (state === "INCOMPLETE") {
                // do something
            }
                else if (state === "ERROR") {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            console.log("Error message: " + 
                                        errors[0].message);
                        }
                    } else {
                        console.log("Unknown error");
                    }
                }
        });
        $A.enqueueAction(action);
    }
})

Let me know if this solution makes sense to your problem. 

All Answers

sachin kadian 5sachin kadian 5
I think you can use getContent Method in visualforce page Controller . 

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_System_PageReference_getContent.htm

You can get the content of vf page and create an attachment on opportunity with this
sachin kadian 5sachin kadian 5
i just tried this and was able to create the attachment successfully - 

I created one vf page that renders a PDF
 
<apex:page controller="TextVFPDFController" renderAs="PDF">
    {!opp.Name}
</apex:page>

I am just showing opportunity name in PDF. Here is the controller for vf page - 
 
public class TextVFPDFController {
    public Opportunity opp{get;set;}
    public TextVFPDFController(){
        Id oppId = apexpages.currentpage().getparameters().get('id');
		opp = [select id,Name from opportunity where id=: oppId];
    }
}

Now i created one lightning component  which has only one save button , on click of this button , it will create same pdf and attach it to an account 
 
public class TestAppController {
	@auraEnabled
    public static void savePDFOpportunity(){
        PageReference pdfPage = new PageReference('/apex/TextVFPDF');
		pdfPage.getParameters().put('Id', '0062800000C044o');
        Blob pdfContent = pdfPage.getContent();
        
        Attachment attach1= new Attachment();
        attach1.ParentId = '0010K00001e2kcG';
        attach1.Name = 'Test Attachment for PDF';
        attach1.Body = pdfContent;
        attach1.contentType = 'application/pdf';
		insert attach1;
        
    }
}
 
<aura:application controller="TestAppController">
	<lightning:button variant="brand" label="Save" onclick="{! c.savePDF }" />
</aura:application>
 
({
    savePDF : function(cmp, event, helper) {
        var action = cmp.get("c.savePDFOpportunity");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                alert('Attachment saved successfully');
                
            }
            else if (state === "INCOMPLETE") {
                // do something
            }
                else if (state === "ERROR") {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            console.log("Error message: " + 
                                        errors[0].message);
                        }
                    } else {
                        console.log("Unknown error");
                    }
                }
        });
        $A.enqueueAction(action);
    }
})

Let me know if this solution makes sense to your problem. 
This was selected as the best answer
Bertrand DBBertrand DB
Thank you Sachin, I still have a question.

How should I replace the hard-coded id here?
public class TestAppController {
	@auraEnabled
    public static void savePDFOpportunity(){
        PageReference pdfPage = new PageReference('/apex/TextVFPDF');
		pdfPage.getParameters().put('Id', '0062800000C044o');
        Blob pdfContent = pdfPage.getContent();
        
        Attachment attach1= new Attachment();
        attach1.ParentId = '0010K00001e2kcG';
        attach1.Name = 'Test Attachment for PDF';
        attach1.Body = pdfContent;
        attach1.contentType = 'application/pdf';
		insert attach1;
        
    }
}

The id in these two lines:
pdfPage.getParameters().put('Id', '0062800000C044o');

And
attach1.ParentId = '0010K00001e2kcG';

Should actually be the something like "apexpages.currentpage().getparameters().get('id');" ?
sachin kadian 5sachin kadian 5
From your lightninig component, you can pass the id
action.setParams({
    'recordId' : 'id you want to send',
    'parentId' : 'id of parent'
})

you can receive it in apex controller -
 
public class TestAppController {
	@auraEnabled
    public static void savePDFOpportunity(String recordId,String parentId){
        PageReference pdfPage = new PageReference('/apex/TextVFPDF');
		pdfPage.getParameters().put('Id', recordId);
        Blob pdfContent = pdfPage.getContent();
        
        Attachment attach1= new Attachment();
        attach1.ParentId = parentId;
        attach1.Name = 'Test Attachment for PDF';
        attach1.Body = pdfContent;
        attach1.contentType = 'application/pdf';
		insert attach1;
        
    }
}


 
Bertrand DBBertrand DB
Thanks a lot for your help! I managed to do what I wanted.
lakshmi surendranlakshmi surendran
Hi Sachin, This code really helps me. But I need to download the pdf instead of attachment. Can you suggest if you have any idea.

Thanks.
Kashmira PuranikKashmira Puranik
User-added image
I am trying to create this on a custom object as it is on the Quote object. I have already created vf page which generates the pdf. But I am not able to create those buttons and save it as an attachment to that custom object. Please advise on this.
Vijay Kumar YadavVijay Kumar Yadav
How to add multiple Id's(value) into the put parameter method on the same Id key . I tried it using loop but it is overidding the values and accepting the last value so tell me how to do that