I have a VF page in which I am displaying data to users. The data is fetched from an external system. I wish to have a download button on the VF page so that users will be able to download the data in csv format.
On researching, I found out that I can redirect to a VF page that defines contentType
and the file will get downloaded automatically. The problem is that another call will need to be made on that VF page. Is there a way by which I can download the data without making another call to fetch the data?
Example Code
Controller Code
public class ExportContactsDemoController {
public List<Contact> lstContact {set;get;}
public ExportContactsDemoController(){
lstContact = [Select Id, Name, Email, Phone From Contact Limit 10];
}
}
ShowContactsDemo – This VF page displays data to the users:
<apex:page controller="ExportContactsDemoController" cache="true">
<apex:form id="f1">
<head>
<script>
function ExportToExcel(){
confirm('Are you sure you want to export an excel ?');
window.open('/apex/ExportContactsDemo');
}
</script>
</head>
<body>
<apex:pageBlock title="10 Contacts">
<apex:commandButton value="Export to Excel" onclick="ExportToExcel();" oncomplete=""/>
<apex:pageBlockTable value="{!lstContact}" var="objContact">
<apex:column value="{!objContact.Name}"/>
<apex:column value="{!objContact.Email}"/>
<apex:column value="{!objContact.Phone}"/>
</apex:pageBlockTable>
</apex:pageBlock>
</body>
</apex:form>
</apex:page>
ExportContactsDemo – This VF page uses the same controller as previous page and downloads the data
<apex:page controller="ExportContactsDemoController" contentType="application/vnd.ms-excel#ContactsData.xls">
<apex:pageBlock >
<apex:pageBlockTable value="{!lstContact}" var="objContact">
<apex:column value="{!objContact.Name}"/>
<apex:column value="{!objContact.Email}"/>
<apex:column value="{!objContact.Phone}"/>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:page>
In this example, the query present inside the controller is fired twice. Is there a way I can use the data fetched by the first query and download it without querying again?
Best Answer
Yes, you can avoid the second query, but you won't be able to open it in a new window. To do this, redirect from the first page to the second through an
apex:
component that can redirect via anaction
. When you do this, Visualforce will pass the controllers between pages and retain the View State of the page, thus not calling the controller again.I've written a very simple example you can follow along with:
commonController.apxc
page1.vfp
page2.vfp
I've intentionally made this a PDF so it can be as simple as possible, but you can do this sort of redirect easily for any type of content type that's supported.
You could also use a merge field for
renderAs
, simply changing from HTML to Excel when you change a variable, etc, although I agree that keeping the pages separate is usually a better idea.A slightly less popular approach is to use a temporary record or file somewhere in Saleforce (e.g. a ContentVersion), which allows transfer of data that would otherwise exceed the view state of 135kb.