Skip to content

Salesforce Screen Flow – Download records into a CSV file with specified fields using the collection variable

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