I have a Lightning Component that renders data in a table. I currently have the logic that downloads as a CSV file. For some users,Excel is not auto formatting the file to display as Columns. So they want the file to be downloaded as an Xls or Xlsx file. I have done some research to see if there is a solution for downloading as an excel file on the client side itself but I couldn't find any. The logic that I currently have is below :
csvSample.cmp
<aura:component controller="csvDownloadCtrl"> <aura:handler name="init" value="{!this}" action="{!c.loadContactList}"/> <aura:attribute name="ListOfContact" type="contact[]"/> <div class="slds-m-around--xx-large"> <button class="slds-button slds-button--brand" onclick="{!c.downloadCsv}">Download As CSV</button> <br/><br/><table class="slds-table slds-table--bordered slds-table--cell-buffer"><thead><tr class="slds-text-title--caps"><th class="slds-is-sortable slds-text-title--caps" scope="col"><span class="slds-truncate" title="Name">First Name</span> </th><th class="slds-is-sortable slds-text-title--caps" scope="col"><span class="slds-truncate" title="Last Name">Last Name</span></th><th class="slds-is-sortable slds-text-title--caps" scope="col"><span class="slds-truncate" title="Department">Department</span></th><th scope="col"><div class="slds-truncate" title="MobilePhone">Mobile Phone</div></th></tr></thead><!--table body start, Iterate contact list as a <tr> --><tbody><aura:iteration items="{!v.ListOfContact}" var="con"> <tr><th scope="row"><div class="slds-truncate" title="{!con.FirstName}">{!con.FirstName}</div></th><th scope="row"><div class="slds-truncate" title="{!con.LastName}">{!con.LastName}</div></th><th scope="row"><div class="slds-truncate" title="{!con.Department}">{!con.Department}</div></th><th scope="row"><div class="slds-truncate" title="{!con.MobilePhone}">{!con.MobilePhone}</div></th> </tr></aura:iteration></tbody></table> </div></aura:component>
csvSampleController.js:
({ loadContactList: function(component, event, helper){ helper.onLoad(component, event); }, downloadCsv : function(component,event,helper){ var stockData = component.get("v.ListOfContact"); var csv = helper.convertArrayOfObjectsToCSV(component,stockData); if (csv == null){return;} var hiddenElement = document.createElement('a'); hiddenElement.href = 'data:text/csv;charset=utf-8,'+ encodeURI(csv); hiddenElement.target = '_self'; hiddenElement.download = 'ExportData.csv'; document.body.appendChild(hiddenElement); hiddenElement.click(); }, })
csvSampleHelper.js:
({ onLoad: function(component, event) { var action = component.get('c.fetchContact'); action.setCallback(this, function(response){ var state = response.getState(); if (state === "SUCCESS") { component.set('v.ListOfContact', response.getReturnValue()); } }); $A.enqueueAction(action); }, convertArrayOfObjectsToCSV : function(component,objectRecords){ var csvStringResult, counter, keys, columnDivider, lineDivider; if (objectRecords == null || !objectRecords.length) { return null; } columnDivider = ','; lineDivider = '\n'; keys = ['FirstName','LastName','Department','MobilePhone','Id' ]; csvStringResult = ''; csvStringResult += keys.join(columnDivider); csvStringResult += lineDivider; for(var i=0; i < objectRecords.length; i++){ counter = 0; for(var sTempkey in keys) { var skey = keys[sTempkey] ; if(counter > 0){ csvStringResult += columnDivider; } csvStringResult += '"'+ objectRecords[i][skey]+'"'; counter++; } csvStringResult += lineDivider; } return csvStringResult; },})
csvDownloadCtrl.apxc :
public class csvDownloadCtrl {@AuraEnabled public static list <contact> fetchContact(){ List <contact> returnConList = new List < contact > (); for(contact con: [SELECT firstName, LastName, Department, MobilePhone From contact LIMIT 1000]) { returnConList.add(con); } return returnConList; }}
PS : The data that is being downloaded is a list of Wrapper object. For keeping things simple I have added the Apex controller to return list of contacts. Also the use can filter the data on the component, So I have to download the data that is available on the Component after filtering.
Any help is greatly appreciated. Thanks