This post is a continuation from the previous post that described how to show data in a JQGrid using Javascript Remoting. I started it but not sure if it is able to generate c CSV for me, so first I tried passing in a simple array and thanks to my positive thinking it did generate a CSV!!
So, why did I experiment with Downloadify to generate the CSV from the client side?
There was a requirement to generate the CSV file from the salesforce data containing more than 40,000 records, I could have used a visualforce page to render as a CSV, but the issues are:
a. Visualforce components cannot hold more then 1000 records.
b. Tried HTML components, but got caught in various limitations of apex.
What did I use to acheive the functionalty?
I used a open-source JQuery plugin "Downloadify" that generates CSV files at the client side using a Adobe Flash plugin. Copying the text directly from GitHub:
"This library is a tiny JavaScript + Flash library that allows you to generate files on the fly, in the browser, without server interaction. Web applications that allow you to generate vCards, color palettes, custom code, etc would benefit from using this library. In addition to increasing speed (no round trip to the server) this solution can reduce the database and server load of existing web applications. This is not a library to ‘force download’ a file from a server. It does not interact with a server at all"
Find more details about the plugin at publishers Github page:
https://github.com/dcneiner/Downloadify
More to that, here is a video from publishers web-site:
<iframe src="http://player.vimeo.com/video/7735312" width="400" height="296" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
What are the references being used for arriving at this awesome workaround?
1. http://dougneiner.com/post/255998581/downloadify
2. http://davidwalsh.name/downloadify
3. A hundred thanks to this blog : http://geekswithblogs.net/JoostPloegmakers/archive/2005/06/17/44045.aspx, it helped me to retrieve data from the javascript array.
Visualforce Code:
<apex:page standardController="Contact" sidebar="false" showHeader="true" standardStylesheets="true" extensions="DeveloperExtension2" id="page1" >
<link rel="stylesheet" type="text/css" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/themes/redmond/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="http://www.trirand.net/aspnetmvc/Content/themes/ui.jqgrid.css" />
<script type="text/JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"/>
<script type="text/javascript" src="http://www.trirand.net/aspnetmvc/Scripts/trirand/i18n/grid.locale-en.js"/>
<script type="text/javascript" src="http://www.trirand.net/aspnetmvc/Scripts/trirand/jquery.jqGrid.min.js"/>
<apex:includeScript value="{!URLFOR($Resource.Downloadify, '/dcneiner-Downloadify-652377f/js/swfobject.js')}"/>
<apex:includeScript value="{!URLFOR($Resource.Downloadify, '/dcneiner-Downloadify-652377f/src/downloadify.js')}"/>
<script type="text/JavaScript">
var gridData;
var obj;
function search(jsonString) {
$("#pdata").jqGrid("GridUnload");
gridData=JSON.stringify(jsonString);
obj = JSON.parse(gridData);
jQuery("#pdata").jqGrid({
data: jsonString,
datatype: 'local',
colNames:['Name','Email','Account Name'],
colModel:[
{name:'Name',index:'Name', width:40},
{name:'Email',index:'Email', width:40},
{name:'Account Name',index:'Name', width:40}],
rowNum:10,
rowList:[5,10,20,30,50,100, 1000],
pager: '#ppdata',
sortname: 'name',
viewrecords: true,
sortorder: "desc",
caption:"Contact Data",
width: 800,
height: 180,
});
$("#pdata").trigger("reloadGrid");
}
function showDataInJqGrid(){
var accName = document.getElementById("query").value;
contactSearch(accName);
}
function contactSearch(name) {
var jsonString;
myp1.DeveloperExtension2.showContacts(name,handleContacts);
}
function handleContacts(result, event) {
if(event.type == 'exception'){
alert(event.message);
}else{
jsonString = result;
search(jsonString);
gridData = JSON.stringify(jsonString);
}
}
function downloadRecords(){
var downloadData = 'Name,Email\r\n';
for(var i = 0; i < jsonString.length; i++) {
var obj = jsonString[i];
downloadData += obj.Name + ',' + obj.Email + '\r\n';
}
Downloadify.create('downloadify',{
filename: function(){
return 'file1.csv';
},
data: function(){
return downloadData;
},
onComplete: function(){
alert('Your File Has Been Saved!');
},
onCancel: function(){
alert('You have cancelled the saving of this file.');
},
onError: function(){
alert('You must put something in the File Contents or there will be nothing to save!');
},
dataType: 'string',
transparent: false,
swf: "{!URLFOR($Resource.Downloadify, '/dcneiner-Downloadify-652377f/media/downloadify.swf')}",
downloadImage: "{!URLFOR($Resource.Downloadify, '/dcneiner-Downloadify-652377f/images/download.png')}",
width: 100,
height: 30,
transparent: true,
append: false
});
}
</script>
<apex:sectionHeader title="Contact Search using AccountID" subtitle="Search Contact" />
<apex:pageBlock mode="maindetail">
<apex:form id="qform" >
<apex:pageBlockSection title="Search Contact for the Account" collapsible="false" columns="1" >
<table width="100%">
<tr>
<td><h3>Enter Account Name </h3> <input type="text" id="query" />
<input type="button" value="Show Contacts" class="btn" onclick="showDataInJqGrid();" />
<input type="button" value="Download Records" class="btn" onclick="downloadRecords();" />
</td>
</tr>
</table>
</apex:pageBlocksection>
</apex:form>
<apex:pageBlockSection title="Contacts in Response" collapsible="false" rendered="true">
<div id="response" style="font-size: 16px;width: 300px;font-family: monospace; font-stretch: expanded" />
<table id="pdata"></table>
<div id="ppdata"></div>
<div id="downloadify"></div>
</apex:pageBlocksection>
</apex:pageblock>
</apex:page>
Controller:
global class DeveloperExtension2 {
public DeveloperExtension2(ApexPages.StandardController controller){}
@RemoteAction
global static List<Contact> showContacts(String accName){
accName = '%'+ accName+'%';
List<Contact> lst_contacts = new List<Contact>([select id, name, email, Account.Name from contact /* limit 10 Account.Name LIKE : accName */ ]);
List<Contact> lst_Contact = new List<Contact>();
for(Contact c : lst_contacts){
c.Email= 'RemoteTestContact@sdf.com';
lst_Contact.add(c);
}
system.debug('###### ' + JSON.serialize(lst_Contact));
return lst_Contact ;
}
}
How to use it?
It's simple, you need to enter the AccountName and click on show contacts and it will populate the contacts for the AccountName provided. If you will leave it blank the table will show all the contacts where account is null.
Now, click on "Download Records" and keep your eyes below the bottom left corner of the JQGrid, you will see a button "Save to Disk", click on it and you will be asked for the location to save the file. Enter your file name and appending ".csv" will yield you a CSV file containing the records.
Isn't it simple?
Some advantages that can be briefed are:
1. Less processing time, its only for running the SOQL on the server.
2. No Server side processing of data, and the client side is JS processing is much faster.
3. Good riddance from apex/visualforce issues such as Heap size, View State etc.
4. I have tried using it for more than 45K records and to my surprize it was successfull!!
NOTE: Please not that "myp1" is the namespace for my Org, so, if you using any namespace then can prefix it, or if you are not using it then remove it.