This article demonstrates exporting records collection into a CSV file with specified fields using the screen flow. First, create an apex class as mentioned below. This apex class takes a list of sObject, object name, and comma-separated field names and returns a list of sObject that only contains the fields to be exported into CSV.
Apex Class Name: CsvDataExporterController
public without sharing class CsvDataExporterController {
@AuraEnabled(cacheable = true)
public static List<sObject> fetchRecords(list<sObject> records, String objectName, String fieldNames) {
system.debug(records);
list<String> RecordIds = new list<String>();
for(sObject obj: records) {
recordIds.add((String)obj.get('Id'));
}
system.debug(recordIds);
String queryString = 'SELECT '+fieldNames.removeEnd(',')+' FROM '+objectName+' WHERE ID IN : recordIds';
system.debug('queryString::'+queryString);
List<sObject> sObjectList = Database.query(queryString);
system.debug('sObjectList before::'+sObjectList);
list<String> fieldList = fieldNames?.split(',');
for (sObject obj : sObjectList){
Map<String, Object> acctMap = obj.getPopulatedFieldsAsMap();
for (String field : fieldList){
if (!acctMap.containsKey(field.trim())){
obj.put(field.trim(), NULL);
}
}
}
system.debug('sObjectList after::'+sObjectList);
return sObjectList;
}
@AuraEnabled(cacheable = true)
public static List<Map<String,String>> getColumnData(String objectName, String fieldNames){
system.debug('objectName::'+objectName);
system.debug('fieldsNames::'+fieldNames);
List<Map<String,String>> columnsData = new List<Map<String,String>>();
Map<String,Schema.SObjectField> apiToLabelNameMap = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap();
list<String> fieldNamesList = fieldNames?.split(',');
for(String fieldName : fieldNamesList){
Map<String,String> labelNameMap = new Map<String,String>();
Schema.DescribeFieldResult fieldResult = apiToLabelNameMap.get(fieldName.trim()).getDescribe();
labelNameMap.put('label',fieldResult.getLabel());
labelNameMap.put('fieldName',fieldName.trim());
columnsData.add(labelNameMap);
}
return columnsData;
}
}
Now create a Lightning web component with the name csvDataExporter. This LWC takes input data from the screen flow and provides a button to download the data into a CSV file. Moreover, the LWC also display the data with the specified fields in a datatable that is going to be exported so that user can preview the data before exporting it.
csvDataExporter.html
<template>
<div class="slds-var-p-around_small">
<lightning-button variant="brand" label="Export Data" title="Export Data Into CSV" icon-name="utility:download"
onclick={clickHandler} disabled={checkRecord}></lightning-button>
</div>
<template if:true={isLoaded}>
<lightning-datatable key-field="Id" data={data} columns={columns} hide-checkbox-column show-row-number-column>
</lightning-datatable>
</template>
<template if:false={isLoaded}>
<lightning-spinner alternative-text="Loading" size="large" variant="brand"></lightning-spinner>
</template>
</template>
csvDataExporter.js
import { LightningElement, wire, api } from 'lwc';
import fetchRecords from '@salesforce/apex/CsvDataExporterController.fetchRecords';
import getColumnData from '@salesforce/apex/CsvDataExporterController.getColumnData';
export default class CsvDataExporter extends LightningElement {
@api records;
@api objectName;
@api fieldNames;
data = [];
columns = [];
isLoaded = false;
connectedCallback() {
getColumnData({objectName : this.objectName, fieldNames : this.fieldNames})
.then( results => {
if(results){
this.columns = [...results, ...this.columns];
console.log('results : ',results);
}
});
}
@wire(fetchRecords, {records : '$records', objectName: '$objectName', fieldNames: '$fieldNames'}) wiredFunction({data, error}) {
if(data) {
console.log(data);
this.data = data;
this.isLoaded = true;
} else if(error) {
this.isLoaded = true;
console.log(error);
}
}
get checkRecord() {
return this.data.length > 0 ? false : true;
}
clickHandler() {
let downloadRecords = [...this.data];;
let csvFile = this.convertArrayToCsv(downloadRecords)
this.createLinkForDownload(csvFile);
}
convertArrayToCsv(downloadRecords) {
if (downloadRecords.length === 0) {
return '';
}
// Get all possible keys from all records
const allKeys = [...new Set(downloadRecords.flatMap(record => Object.keys(record)))];
const csvHeader = allKeys.join(',');
console.log('header: ' + csvHeader);
const csvBody = downloadRecords.map((currItem) => {
return allKeys.map(key => {
let value = currItem[key];
if (value === null || value === undefined) {
return '';
}
// Handle values that contain commas or newlines by wrapping them in double quotes
if (typeof value === 'string' && (value.includes(',') || value.includes('\n'))) {
return `"${value}"`;
}
return value;
}).join(',');
});
console.log('body: ' + csvBody);
const csvFile = csvHeader + "\n" + csvBody.join("\n");
return csvFile;
}
createLinkForDownload(csvFile) {
const downLink = document.createElement("a");
downLink.href = "data:text/csv;charset=utf-8," + encodeURI(csvFile);
downLink.target = '_blank';
downLink.download = "Exported_Data.csv"
downLink.click();
}
}
Make sure the LWC is visible for screen flows.
csvDataExporter.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>CSV Data Exporter</masterLabel>
<description>This component exports the collection variable data into a CSV file.</description>
<targets>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<propertyType name="T" extends="SObject" label="Select sObject" description="Generic sObject data type used for input sObject properties" />
<property name="records" type="{T[]}" label="SObject List to download" />
<property name="objectName" type="String" label="Salesforce object API Name"/>
<property name="fieldNames" type="String" label="Comma separated field API names"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Screen flow input
Screen flow result