Adding Lightning Web Components to Flow Screens

For the last two years, Flow creators have been able to add lightning component to Flow Screens. This has powerfully extended the capability of screens. We’ve had a great time creating reusable components as a community, and many of those components are available for use to all parties.

The underlying web technology enabling these components, known as Aura, has served Salesforce well, but it’s more than 5 years old, and that’s a long time when it comes to changes in web technology. Salesforce recently released a major upgrade to its web technology called Lightning Web Components, and it’s now possible to embed this new generation of components into Flow screens. Let’s take a look at how it’s done.

In this demonstration, we create an LWC that allows the user to manually add and manage sharing settings on a record. This allows a Sharing button to be placed on Lightning record pages. The guts of this component consist of code written by resident Salesforce evangelist Shane McLaughlin.

To get an LWC to appear in Flow’s Screen Builder, you add a ‘lightning_FlowScreen’ target to the component’s meta file:

Attributes and Properties

To expose attributes from the LWC, you first use @api in the LWC’s javascript, as is standard for any property you want to make accessible outside of the component:

Then you additionally specify which of these public properties should be made visible in Flow by adding a targetConfig section:

The type field supports the same types that Flow supports with aura components. That includes specific Sobjects like “Contact” and specific SObject collections like “Account[]”. You can also use this interface to directly publish access to your apex class data structures for declarative manipulation in Flow . This extremely powerful technology is discussed at length here.

Note the following syntax for specifying SObjects and Apex Classes:

  • @salesforce/schema/<name of sObject>
  • apex://<name of Apex class>

Add array notation if it is a collection:  []

As of Summer ’20, you can now use generic SObjects. Here’s an example:

<targetConfig targets="lightning__FlowScreen">
    <propertyType name="T" extends="SObject" label="Object API Name" description="Select the api name of the SObject this component is going to be looking for" />

    <property name="selectedRecord" type="{T}" label="The selected Record" role="outputOnly" description="The SObject the user has selected" />
</targetConfig>
<targetConfig targets="lightning__FlowScreen">
    <propertyType name="T" extends="SObject" label="Object API Name" description="Select the api name of the SObject this component is going to be looking for" />

    <property name="selectedRecord" type="{T}" label="The selected Record" role="outputOnly" description="The SObject the user has selected" />
</targetConfig>

See this post for more detail.

Namespaces

With a namespaced org you have to include the namespace in the type string using the namespace prefix. So, for SObjects it would look like type=”@salesforce/schema/myNs__CoolCustomObject__c”. For apex types:”apex://myNs__MyApexClass”

Input and Output Control via Roles

The ‘role’ parameter can be added to property elements inside of targetConfigs. It has been created specifically to provide support for Flow’s ability to limit access to inputOnly or outputOnly. If no role is specified, the property is available in both directions.

Troubleshooting
If you hit this error: unable to find field dataType for complex reference: foo, you may need to set a default value on your attribute. For example, this error will occur if the value attribute is not set to an empty string here:

Example: Creating Contacts in Javascript and Passing Them to Flow

The component Contacter exposes two attributes. One is a Contact and the other is a collection of Contacts:

In the Flow, the outputs from these attributes are mapped manually to corresponding Flow resources:

Here’s an example of the corresponding javascript that creates new contacts and assigns them to the public attributes where Flow can get them:

Considerations When Working With Targets

Be Careful about Mixing Your Targets

The support mentioned above for SObjects and Apex Classes works only for Flow and not for other targets like lightning__RecordPage. So something like this will fail:

This can be hard to debug because each target has its own validation and the errors reported back don’t specify which target is the source of the difficulty. So you can get into a situation where you think your new Flow interface is complaining when it’s actually a problem being reported by App Builder.

Each property has to meet the requirements of each Target that mentions it.

Example: App Builder (and thus lightning__RecordPage, lightning__AppPage, lightning__HomePage) requires that if you add a ‘required’ attribute you also have to add a ‘default’ attribute. Flow does not have this requirement. If you try to add the same attribute to both sections, you’ll have to satisfy the combination of their constraints.

A good practice is to separate the App Builder targets from the Flow target, like this:

This helps but does not eliminate all possible interaction headaches. In the example above, Flow is forced to add a default attribute to editTabName because editTabName is also used in the AppBuilder targets, where that required/default issue is enforced. But it makes it easier to debug because you can comment out one set and see which validation is causing problems.

Events and Notification

Unlike Aura, the parent component doesn’t automatically learn when something changes in the child component. This takes some getting used to for old Aura hands.

There are two scenarios where this matters. For all of your LWC’s, think of the Flow Runtime as the parent container that needs to be notified about changes that need to be available external to your component. Any attribute that you intend to be used downstream in the Flow must be constantly reported to the Flow Runtime whenever the value changes in your component. (Likewise, if you have an lwc with lwc child components, you’ll have similar issues).

To notify Flow of changes in the value of an attribute in your component, the component must fire the FlowAttributeChangeEvent event.

In the following example, a ToDo LWC dispatches a FlowAttributeChangeEvent when a new item is added to the list of To Do’s:

import { LightningElement, api, track } from 'lwc';
import { FlowAttributeChangeEvent, FlowNavigationNextEvent } from 'lightning/flowSupport';
export default class Todos extends LightningElement {
    @api
    availableActions = [];
    @api
    get todos() {
        return this._todos;
    }
    set todos(todos = []) {
        this._todos = todos;
    }
    @track _todos = [];
    get hasTodos() {
        return this._todos && this._todos.length > 0;
    }
    handleUpdatedText(event) {
        this._text = event.detail.value;
    }
    handleAddTodo() {
        this._todos.push(this._text);
        // notify the flow of the new todo list
        const attributeChangeEvent = new FlowAttributeChangeEvent('todos', this._todos);
        this.dispatchEvent(attributeChangeEvent);
    }
    handleGoNext() {
        // check if NEXT is allowed on this screen
        if (this.availableActions.find(action => action === 'NEXT')) {
            // navigate to the next screen
            const navigateNextEvent = new FlowNavigationNextEvent();
            this.dispatchEvent(navigateNextEvent);
        }
    }
}

See additional pieces of the To Do example

Validation

To provide custom validation, create a function called “validate” in your js file:

@api
validate() {
    if(/* true conditions */) { 
         return { isValid: true }; 
        } 
    else { 
    //If the component is invalid, return the isValid parameter as false and return an error message. 
         return { 
               isValid: false, 
               errorMessage: '/*A message that helps your user enter a valid value or explains what went wrong.*/' 
                }; 
     } 
}

Reference Documentation

Navigation

To programmatically transition a Flow with an LWC component, you dispatch an event using this syntax:

import { LightningElement, api } from 'lwc';
import { FlowNavigationNextEvent } from 'lightning/flowSupport';

export default class NameOfYourComponent extends LightningElement {

   @api
   handleGoNext() {
       const nextNavigationEvent = new FlowNavigationNextEvent();
       this.dispatchEvent(nextNavigationEvent);
   }
}

The supported events are:

  • FlowNavigationNextEvent
  • FlowNavigationBackEvent
  • FlowNavigationFinishEvent
  • FlowNavigationPauseEvent

Data Sources

Attributes that use data sources will not show up in Flow Builder as mappable inputs or outputs. The simplest thing to do here is just assume that Flow doesn’t work with datasource properties yet. However, the attribute will function properly. So for example, you could use an attribute with a datasource to populate a picklist. But you could not directly pass in a default value for that picklist. You’d have to do it indirectly, passing in the value to an attribute that Flow can see and then copying it once you’re in your code.

Wire Service Considerations

It can be a little tricky to get wire services interacting properly with incoming flow data. Details here.

Examples

Set Sharing Manually

Sample Gists

https://gist.github.com/alexed1/e3e580103375120ca2dc1374bb4f73ff