Let’s Revisit How to use both the Selected and the Edited records in a Datatable

A year ago, I showed you how you can use a Flow with a Loop and a special Apex action to update the Selected records from the Datatable component with the Edited records from the same Datatable.

Now I’m going to show you how you can get rid of the Loop and use a different action and a simple assignment to produce the same results.

My Datatable Flow Screen Component allows a user to both Select records and make Edits to records.  The component returns two separate collection variables.  One of them includes the original values of just the Selected records.  The other one includes just the Edited records whether they were selected or not.

Sometimes, you may want to process just the selected records in your flow, but include the edited values in those selected records.  This sample flow shows how you can create a selected record collection with edits.  The flow uses the Get Common and Uncommon Records action that is part of the Collection Actions from UnofficialSF.com to compare the collections returned by the Datatable and extract the common and unique records from them.  

Here’s a Datatable displaying all of the Product records from an Opportunity.

I’ll select 3 records and make edits to 2 of them.

My normal outputs from the Datatable include the original 3 selected records and a separate collection of just the edited records.

To create a record collection that combines these two requires just a couple extra steps in your flow.  The magic happens in the Get Common and Uncommon Records Flow Action.  This action takes two separate record collections, Source (Selected) and Target (Edited), along with a field from each (Id) that is used to match them up and returns 4 separate record collections.

  • Source Common – Records from the Source collection that are also in the Target collection
  • Source Unique – Records from the Source collection that are not in the Target collection
  • Target Common – Records from the Target collection that are also in the Source collection
  • Target Unique – Records from the Target collection that are not in the Source collection

First, I created a Record Collection variable for Opportunity Product that will be used to store the combined outputs from the Action.

I pass the outputs from the Datatable into the Get Common and Uncommon Records Action.

We are only going to use two of the output collections and they will be combined into a single collection of Selected records that include any of the Edits made to them.

  • Source Unique – All Selected records that are not also Edited records
  • Target Common – All Edited records that are also Selected records

An Assignment node is used to Add each of the desired collection outputs from the Action into our final record collection of selected and edited records.

As you can see in the Results Screen, the Selected Records are unique from the Edited Records, but the combined Selected Products with Edits includes all Selected with any Edits made to those records.

Validation Checker Flow Action

Validation Checker Flow Action

Created by Eric Smith

See https://unofficialsf.com/validation-checker-flow-action/ for the most up to date information on this component.

Don’t your users just love it when they see this on their screen?

The fun really starts when you get this email in your inbox.

You could certainly take the time and effort to add a bunch of decision elements and recreate all of your validation rules in your flow.  I bet it would be even more exciting trying to keep all of that maintained.  

Wouldn’t it be nice if your Flows could check for and trap validation rule failures, missing required fields and text overruns?

Well now they can with this simple Check Validation flow action.  

Before you attempt a Create Records or Update Records element in your Flow, add this simple Flow Action and pass it a single record or a collection of records.

This action will check your record(s) and let you know if there would be any Validation Rule errors, field size overruns or missing required field errors if you tried to create or update the record(s).

All this is done before you try to do something that would cause the Flow to fail with an unhandled fault.  You get to determine what to do next and what information you want to present to your user.

You can also use this action as part of your Fault Paths. Instead of just handling the fault without knowing exactly what it is, you can get the error(s) that caused the fault and act on them as you see fit.

In this Flow, a Toast Message will be displayed with any errors and the User will be given a choice of whether or not they want to Try Again or Exit.

You can specify which field from your record you want to include in the error messages to help identify which record(s) failed.

The action returns an isError boolean value along with a text message of the individual errors.  

Validation Rule Failures

Missing Required Field Error

Text Field Size Error

You can even specify an optional input attribute to use this action instead of the Create Records or Update Records to actually perform the inserts and updates if no errors are generated.  If you perform a successful insert of new records, the new record ID(s) will be returned by the action.

Take control of your automations and no more worrying about Flows crashing when User provided values won’t pass Validation Rules or other input errors.


  • If there are multiple types of errors, only one type of error will be returned.
  • If any Text fields are over their size limit, only those errors will be returned.
  • If any Validation Rule fails, all Validation Rule failures will be returned.
  • If any Required Fields are missing, only those errors will be returned.
  • Fields over their size limit are handled first, followed by Validation Rules, followed by Required Fields.


Input RecordSObjectAny Standard or Custom SObject Record
Required: Provide either a Record or Record Collection, not both
Input Record CollectionSObject CollectionAny Standard or Custom SObject Record Collection
Required: Provide either a Record or Record Collection, not both
Record Identifier Field API NameString (Field API Name)The record’s value for this field will be included as part of the error message.
NOTE: The field must be included in the Record or Record Collection.
Optional, Default: Id
If no errors, commit inserted & updated records?BooleanSet to True if you want the action to upsert the record(s) if there are no errors
Optional, Default: False
isErrorBooleanTrue if there were any errors
errorMessagesStringA single string that includes the error message for each failing record
firstInsertedIdStringIf the commit attribute is set to True and there are no errors, this will be the recordId of the first inserted record 
insertedIdCollectionString CollectionIf the commit attribute is set to True and there are no errors, this will be a String collection of the recordIds of all of the inserted records


Production or Developer Version 1.1

Sandbox Version 1.1

View Source

Source Code

How to Use an Apex-Defined Object with the Datatable Flow Component

How to Use an Apex-Defined Object with the Datatable Flow Component

Updated 1/23/21 to reference the v3 version of the Datatable that utilizes a Custom Property Editor.

I’ve updated my Datatable Lightning Web Component for Flow Screens to support a User Defined (also know as an Apex-Defined) object.

See my Flow and Process Builder List View with Batch Delete App for an example.

To work with an Apex-Defined object in your Flow, you need to create an Apex Descriptor Class for the object.


// Apex-Defined Variable Sample Descriptor Class
public with sharing class SampleClassDescriptor {

    // @AuraEnabled annotation exposes the methods to Lightning Components and Flows
    public String field1;

    public String field2;

    public Boolean field3;

    public Integer field4;    

    // Define the structure of the Apex-Defined Variable
    public SampleClassDescriptor(
            String field1,
            String field2,
            Boolean field3,
            Integer field4
    ) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
        this.field4 = field4;

    // Required no-argument constructor
    public SampleClassDescriptor() {}

In your Flow, you can create and use Apex-Defined record and record collection variables by referencing your Apex Class.

All of the fields in your variable will be available to use in the Flow.

In this sample Flow, I am setting field values in individual records as seen above.

I then add each record to the Apex-Defined record collection variable.

The Datatable component expects a serialized string of the object’s records and fields like the text seen here.


Since you can create Apex Flow Actions to work with your Apex-Defined object, I created an action that converts an Apex-Defined record collection to a serialized string that can be passed to the Datatable component. The action will also convert a serialized string back to a record collection. This can be useful in a Flow where you want to act on a collection of selected or edited records that get passed back to the Flow by the Datatable component.

Special Note: Even without an Apex-Defined Class, you can build a String in your Flow formatted as above and use that to populate a datatable.

You can use this code as a template for your own Apex actions designed to work with a Flow.


 *  Sample Apex Class Template to get data from a Flow, 
 *  Process the data, and Send data back to the Flow
 *  This example translates an Apex-Defined Variable 
 *  between a Collection of Object Records and a Seraialized String
 *  Eric Smith - May 2020

public with sharing class TranslateApexDefinedRecords {         // *** Apex Class Name ***

    // Attributes passed in from the Flow
    public class Requests {
        @InvocableVariable(label='Input Record String')
        public String inputString;

        @InvocableVariable(label='Input Record Collection')
        public List<SampleClassDescriptor> inputCollection;     // *** Apex-Defined Class Descriptor Name ***


    // Attributes passed back to the Flow
    public class Results {

        public String outputString;

        public List<SampleClassDescriptor> outputCollection;    // *** Apex-Defined Class Descriptor Name ***

    // Expose this Action to the Flow
    public static List<Results> translateADR(List<Requests> requestList) {

        // Instantiate the record collection
        List<SampleClassDescriptor> tcdList = new List<SampleClassDescriptor>();    // *** Apex-Defined Class Descriptor Name ***

        // Prepare the response to send back to the Flow
        Results response = new Results();
        List<Results> responseWrapper = new List<Results>();

        // Bulkify proccessing of multiple requests
        for (Requests req : requestList) {

            // Get Input Value(s)
            String inputString = req.inputString;
            tcdList = req.inputCollection;


            // Convert Serialized String to Record Collection
            List<SampleClassDescriptor> collectionOutput = new List<SampleClassDescriptor>();   // *** Apex-Defined Class Descriptor Name ***
            if (inputString != null && inputString.length() > 0) {
                collectionOutput = (List<SampleClassDescriptor>)System.JSON.deserialize(inputString, List<SampleClassDescriptor>.class);    // *** Apex-Defined Class Descriptor Name ***

            // Convert Record Collection to Serialized String
            String stringOutput = JSON.serialize(tcdList);


            // Set Output Values
            response.outputString = stringOutput;
            response.outputCollection = collectionOutput;

        // Return values back to the Flow
        return responseWrapper;


public with sharing class TranslateApexDefinedRecordsTest {

    static testMethod void test() {

        List<SampleClassDescriptor> inputList = new List<SampleClassDescriptor>();

        TranslateApexDefinedRecords.Requests testRequest = new TranslateApexDefinedRecords.Requests();

        testRequest.inputString = '[{"field1":"value1","field2":"value2"},{"field1":"value31","field2":"value4"}]';
        testRequest.inputCollection = inputList;

        List<TranslateApexDefinedRecords.Requests> testRequestList = new List<TranslateApexDefinedRecords.Requests>();

        List<TranslateApexDefinedRecords.Results> testResponseList = TranslateApexDefinedRecords.translateADR(testRequestList);
        system.debug('RESPONSE - '+testResponseList);
        system.assertEquals(testResponseList[0].outputCollection.size(), 2);


When you configure the attributes for the Datatable in your Flow, you need to be aware of these settings:

  1. Start by checking Input data is Apex-Defined in the Advanced section
  2. In the Data Source section, enter your Datatable Record String
  3. Also in the Data Source section, enter a Datatable Record String for any Pre-Selected Rows
  4. There is no Column Wizard so you will have to list your Column Field names and other attributes manually.
  5. For Currency, Number and Percent fields the Column Scales attribute lets you specify the number of places to display after the decimal point. The default is 0.
  6. When you are using an SObject collection, the Datatable component gets information about all of the fields from the system. For a User Defined object, you need to specify the Column Type of data for each field. This can be left blank if all of the columns are text fields.
  7. You are required to provide the name of the datatable’s Key Field. All of the values in this field need to be unique in order for the datatable to function correctly.

There are separate output parameters for Selected and Edited User Defined objects as well.

The (User Defined) outputs will be serialized record strings rather than SObject collections. Be sure to reference the correct ones based on how you assigned the True or False value for the Input data is Apex-Defined attribute.

Get the Sample Flow and Source Code here:


Datatable Now Includes a Custom Property Editor

See this in action during DreamTX!

Flow Builder Demos 12/17 (1PM2PM3PM Eastern Time)

The Datatable Flow Screen Component has come a long ways from the original Aura component that included separate attributes for 10 different Salesforce objects in a single component. Once Salesforce supported the ability to pick the desired Object in the Flow Builder, at the time of configuration, it was rebuilt from scratch as a Lightning Web Component. Now Datatable has been reimagined again with the addition of a Custom Property Editor that’s used by the Flow Builder whenever a Datatable is added to a Flow Screen.

This image has an empty alt attribute; its file name is image-18-894x1030.png

Custom Property Editors allow a developer to bypass the standard basic list of all component attributes in the Flow Builder and replace it with a Lightning Web Component that can present a logical and formatted interface for the user to configure the component. The CPE developed for the Datatable component takes this even further by including a button that launches a separate Flow that displays a special Datatable the user can interact with to configure their Datatable. I like to refer to this as my Custom Column Configuration Wizard.

Once installed, this component will appear as Datatable in the Flow Builder. DatatableV2 will still work with your existing Flows and can coexist with the new Datatable.

Here are a few examples showing how to build a Flow with a Datatable, how to configure the Datatable using the Custom Property Editor and how a user can interact with a Datatable. For complete documentation, visit the Datatable page.

Build a Flow with a Datatable

Configure a Datatable with the Custom Property Editor

Interact with a Datatable

Import and Export Flows between Salesforce Orgs

Have you ever wanted to copy or move a Flow or Process Builder from one org to another without having to create a Change Set or rebuild it from scratch?

How about seeing a great Flow that you or someone else has created and would like to share?

Install this Flow in your org if you would like to Export or Import Flows and Process Builders.


Export a Flow

  1. Select Setup> Process Automation> Flows
  2. Open Import/Export Flows

  3. Run the Flow
  4. Select Export, choose your Flow and click Next

  5. You will see the export status while the Flow is being transferred
  6. A success message will display once the Flow has been exported
  7. The exported Flow is saved as a Salesforce File linked to your User record.  To see your Files, click on the Waffle, type in Files and select Files.

  8. To download and save your Flow file so it can be shared, select the drop-down arrow and choose Download.

Import a Flow

  1. Select Setup> Process Automation> Flows
  2. Select Import/Export Flows
  3. Run the Flow

  4. Select Import then click on Upload Files

  5. Pick the Flow file from your computer’s file dialog box.  Hint: The file names for Flows and Process Builders will end with .flow-meta.xml
  6. After the file has been uploaded, select Done then click Next

  7. You will see the status while the Flow is being imported and then deployed to the current org

  8. A success message will display once the Flow has been imported and deployed

NOTE: Objects, fields and referenced components must be available and compatible in the new org in order for the Flow to be deployed successfully.

Release Notes:

1/2/21 – Eric Smith – Version 1.2
Refactored the Flow to use the Flow Base Packs

9/3/20 – Eric Smith – Version 1.1
Updated the Flow Base Components (v1.2.6) to resolve an issue where some Flows would generate an error on Import or Export

9/1/20 – Eric Smith – Version 1.0
Initial Release

Installation Instructions

Source Code


It’s fairly easy to extract the Date portion of a Datetime value in a Flow formula.


If {!datetimeValue} = 7/22/2020 5:00 PM then the formula will return July 22, 2020.

It is a bit trickier to convert a Date value to a Datetime value using a formula. 

DATETIMEVALUE(TEXT({!dateValue}) + ” 00:00:00″)

If {!dateValue} = July 22, 2020 you would want the formula to return 7/22/2020 12:00 AM.  Instead the value returned will be converted to GMT so the result I get in Portland, Maine is 7/21/2020 8:00 PM.  For me, that’s 4 hours ahead of GMT.

I could find no easy way to do time-zone calculations in Flow so I created a Flow Action that would keep everything in my current time-zone.

With this action, my result for July 22, 2020 is 7/22/2020 12:00 AM.

If you want a timestamp of other than 12:00 AM, you can pass in the desired hour, minute and second values.


Date ValueDateThe Date to be used for the Datetime value
Hour Value (Default = 0)Integer(Optional) The Hour value to be used for the Datetime value (0-23)
Minute Value (Default = 0)Integer(Optional) The Minute value to be used for the Datetime value (0-59)
Second Value (Default = 0)Integer(Optional) The Second value to be used for the Datetime value (0-59)


Created by – Eric Smith – July 2020

Unmanaged v1.0 (Production/Developer)
Unmanaged v1.0 (Sandbox)

Source Code


How to use both the Selected and the Edited records in a Datatable

See an updated version of this post here: Let’s Revisit How to use both the Selected and the Edited records in a Datatable

My Datatable Flow Screen Component allows a user to both Select records and make Edits to records.  The component returns two separate collection variables.  One of them includes the original values of just the selected records.  The other one includes just the edited records whether they were selected or not.

Sometimes, you may want to process just the selected records in your flow, but include the edited values in those selected records.  This sample flow shows how you can create a selected record collection with edits.  The flow loops through each of the selected records and if no edits were made to it, it gets added to the final collection.  If an edited version of the record is available, it gets added to the final collection instead.

Here’s the Select & Edit Screen Datatable displaying all of the Product records from an Opportunity.

I’ll select 3 records and make edits to 2 of them.

My final results show the 3 selected records with the edited fields included.

Let’s go through the steps you can follow to combine the outputs from the Datatable component. In this section, I’ll show you how each of the nodes in the flow is configured.

I start with an Opportunity record with an Id that is passed into the flow.  Whenever possible, let the flow Automatically store all field values.

All of the Opportunity Products are added to a Collection (OpportunityLineItem).

Because I am displaying a similar Datatable multiple times in this flow, I store my attributes in variables so they can be reused for each Datatable.

The Datatable is displayed using the attributes assigned earlier.  Here, the user can select and edit records.

I am manually assigning the outputs from the Datatable to new collection variables to make the steps in this flow more readable.

This loop will take us through each of the records in the collection of Selected records where we will perform the next few steps on each record.  New in Summer ‘20, the flow will automatically create a loop variable for you.

In this step, I look for a record in the collection of Edited records with the same Id as the current Selected record in the loop.  The “Find Records In Collection” Flow Action is part of a group of very powerful actions you can include in your flows to act on record collections.  The entire group can be found and installed from here.

The output from the Flow Action will be the matching record from the collection of Edited records if found, otherwise it will return a null value.  This Decision checks to see if that record was found.

When an Edited record is found, it is added to the final collection variable that we will be using for the rest of our flow.

When there is no matching Edited record, we use the Selected record from the Loop instead.

Whichever record we chose now gets added to the final collection variable.

After the end of the Loop, I display another Datatable with the values from the final collection variable.  This is just an example.  In your own flow you may be doing something here like adding updated products to a new renewal Opportunity or something else with the selected and edited records.



Collection Actions for Flow

Sample Flow (Install in Sandbox)
This includes DatatableV2 and FindRecordsInCollection

Flow and Process Builder List View with Batch Delete

Flow and Process Builder List View with Batch Delete

I’ve been following a couple of ideas on the Idea Exchange for a while now.  One has over 11,000 points and 13 merged ideas.  Process Builder – List View and the other Allow Mass Deletion of Inactive Process Builder Versions has over 4,000 points with 4 merged ideas.

I don’t know about you, but I get pretty frustrated trying to view and keep track of all of my Flows and Process Builders.  A better List View would certainly be appreciated.

When it comes to trying to clean things up and delete some old inactive versions, it becomes downright painful.  

  • Find the Process Builder, 
  • Expand the list, 
  • Find the old version, 
  • Click Delete, 
  • Click Confirm, 
  • Wait,
  • Wait some more, 
  • Go to select the next one — 
  • Oh ^#%$@ I have to start all over again!!!

I decided to put my Datatable component to work and updated it to support Apex-Defined object variables.  Using data pulled from three different Salesforce internal objects, I’m able to display details about Flows and Process Builders.  The full power of the Datatable component comes into play by offering a List View with sorting, highlighting, filtering and more.

Taking advantage of the Datatable’s ability to select multiple records and pass them along in a Flow to be acted upon, I created a Flow Action that interfaces with the Metadata API and handles the batch deletion of the selected inactive Flows and/or Process Builders. 

I’ve bundled all of this together in an App that includes a configurable List View, List View with batch selection for delete and a Flow Picker where you can select a single Flow or Process Builder and then work with a List View of all of its active and inactive versions.

Here’s a video of the component in action.

Installation Instructions


Source Code

Add a New Quick & Smart App Switcher

Here’s a new component that let’s you add a Quick App Switcher to your Record Pages or Utility Bars. Unlike the standard App Switcher, it is smart enough to take you to the same record or page you were on in the App you are switching away from.

In my org I have different Console and Regular Apps for different departments.  In addition to that, I have different Record Types associated with some of those Apps.  When trying to support my users or test different features, I often need to switch Apps.  The standard App Switcher (much improved in Spring 20) still doesn’t take you to the same record or page you were viewing before you switched.

I decided to create my own Lightning Web Component where I could show my most used Apps and navigate to the same page or record I was on when I select the new App.

Because I wanted to have different selections available for different object record pages and to make it easier to implement, I made the component configurable with Custom Metadata.

Each configuration record lists the Apps to include, so all that is needed when you add the component to your page is the name of the Custom Metadata record and the background color you want for the component.  I also used Component Visibility to only show the switcher on the page for System Administrators.

Here is the same component with a different configuration on my Case record pages.

I also wanted a way to implement the component not just on Record Pages but on the Utility Bar as well.  Since LWC components are not aware of the record context on the Utility Bar, I wrapped the component in an Aura component so I could access the recordId.  

Here is another sample configuration of the component, this time added to the Utility Bar for various Apps in my org. 

When I create something like this, I like to keep it as generic as possible so that it can be configured and used by others to fit their own needs.  To help with that (and to give myself a pretty good challenge), I created a Flow that you can run to create your individual Custom Metadata configuration records.  To support this flow, I figured out how to create a LWC datatable component for Flow screens and an Apex component that can write and deploy Custom Metadata records.

Read the Instructions to learn how to install and configure this component.

Install Package
2/20/20 – v1.2 Initial Release – Eric Smith
Unmanaged v1.2 (Production/Developer)
Unmanaged v1.2 (Sandbox)

View Source

Create a Flow Menu for System Admin Tools

System Admin Tools Menu

Over time I have created a number of specialized Flows in my org for System Admins to help fix records or unwind certain processes.  I find these are a much safer way of keeping the data clean rather than trying to remember all the manual steps necessary to update records correctly.

To make it easier to select and run these specialized Flows I wanted to come up with a way to provide a menu of these tools to select from.  On unofficialsf.com, I found a simple Flow Screen Lightning Web Component that could display a list of Flows in the org and return the API Name of the selected Flow.

The first thing I did with this component (flowPickerFSC) was to enhance it by adding some additional attributes to make it more powerful and user friendly.  I added labels and help text for the configuration attributes, created a filter for the flow search, sorted the displayed list, updated the documentation and created install packages for Production and Sandboxes.

One way to use this component to run a flow is to add the OpenURL Flow Action component to the calling flow and pass the URL of the selected flow to it.  I show how this can be done on the documentation page for flowPickerFSC.

For my Admin Tools Menu I wanted to run my flows in place in a pop-up modal window.  Salesforce Labs has a nice app for this on the App Exchange called Launch Flow in Modal.

With just a few minor tweaks to this component, I was able to embed the Flow Picker LWC and use that to select and set the flow to be loaded in the pop-up modal window.

With these changes, my new component is ready to add to any Lightning Home, App or Record page.

  1. – Any random text is OK here as the Flow Name will be reset by the LWC
  2. – This is the standard Salesforce color for Brand (Dark Cerulean)

Here is the component in action:

Here’s hoping this inspires you to try something like this in your own org.

Follow these links for Installation and Source Code:

Source Code



Install Package

This application requires the FlowActionsBasePack to be installed first

Version 1.4 – Eric Smith – 01/02/21



Release History

Version 1.3 – 12/20/19

Lightning Web Component Bundles: flowPickerFSC

Aura Component Bundles:  ccp_launchFlowModal, ccp_renderFlow

Apex Classes: FlowListController, FlowListControllerTest



Eric Smith – December 2019