Flow Local Actions Using Lightning Components

                    Flow Local Actions Using Lightning Components allow you to execute client-side logic in your flow, build or modify custom Lightning components to use as local actions in flows. For example, get data from third-party systems without going through the Salesforce server, or open a URL in another browser tab. Once you configure the Lightning component’s markup, client-side controller, and design resource, it appears in the Cloud Flow Designer as a Local Action element. In this example, the input screen on the flow will take a country name and from there invoke the local action and return the country information from the web service.

How to add Flow Local Actions 

          Add the lightning:availableForFlowActions interface to a Lightning component to make it available as a flow action. When a component is executed as a flow action, the flow calls the invoke method in the client-side controller. Flows that include Lightning components are supported only in Lightning runtime. Lightning components that implement lightning:availableForFlowActions are available as Local Action elements in the Cloud Flow Designer. When a component is executed as a flow local action, the flow calls the invoke method in the client-side controller. To run the code asynchronously in your client-side controller, such as when you’re making an XML HTTP request (XHR), return a Promise. When the method finishes or the Promise is fulfilled, control is returned back to the flow.

Flow Action Component 

Here is the simple component that we will be used as flow action.
<aura:component implements="lightning:availableForFlowActions" access="global">
    <aura:attribute name="countryName" type="String" default="USA" access="global" />
    <aura:attribute name="response" type="String" default="USA" access="global" />
</aura:component>
Controller 
({
    invoke : function(component, event, helper) {
        var cancelToken = event.getParam("arguments").cancelToken;
        return new Promise(function(resolve, reject) {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = $A.getCallback(function() {
                if (this.readyState === 4) { 
                    if (this.status >= 200 && this.status < 300) {
                        var response = JSON.parse(xhttp.responseText);
                        console.log('response'+response);
                        component.set("v.response", JSON.stringify(response));
                        resolve();
                    } else {
                        var errorText = "";
                        if (this.status === 0) {
                            errorText = 'Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.';
                        } else {
                            errorText = this.statusText;
                        }
                        reject(errorText);
                    }
                }
            });
            var countryName = component.get("v.countryName");
            xhttp.open("GET", "https://restcountries.eu/rest/v2/name/"+countryName, true);
            xhttp.send(null);
            cancelToken.promise.then(function(error) {
                xhttp.abort();
                reject(error);
            });
        });
    }
})
When a component is executed as a flow local action, the flow calls the invoke method in the client-side controller. To run the code asynchronously in your client-side controller, such as when you’re making an XML HTTP request (XHR), return a Promise. When the method finishes or the Promise is fulfilled, control is returned back to the flow. When a Promise is resolved, the next element in the flow is executed. When a Promise is rejected or hits the timeout, the flow takes the Local Action element’s fault connector and sets $Flow.FaultMessage to the error message. By default, the error message is “An error occurred when the elementName element tried to execute the c:FlowAction component.” To customize the error message in $Flow.FaultMessage, return it as a new Error object in the reject() call. When a Promise is fulfilled, the next element in the flow is executed. When a Promise is rejected or times out, the flow takes the Local Action element’s fault connector and sets $Flow.FaultMessage to the error message. By default, the error message is “An error occurred when the flow tried to execute the c:FlowAction component.”. To customize the error message, return it as an error in the reject() call.

Configure the designer 

To make an attribute’s value customizable in the Cloud Flow Designer, add it to the component’s design resource. That way, flow admins can pass values between that attribute and the flow when they configure the Local Action element. That way, flow admins can pass values between that attribute and the flow when they configure the corresponding Local Action element.

<design:component>
    <design:attribute name="countryName" label="String"  />
    <design:attribute name="response" label="String"  />
</design:component>

Invoke Action from the Flow 

Now go to the cloud flow designer and create a new screen element to enter the country name

Add the Flow locale actions into the screen and configure the input params as shown below.

Create a screen element to show the output response as shown below.
Final screen flow looks like below

Testing 

Synchronous Code

the example code we have seen so far is Asynchronous Code where we will get the promise and here is the synchronous version of the controller.js

({
    invoke : function(component, event, helper) {
        var cancelToken = event.getParam("arguments").cancelToken;
        var xhttp = new XMLHttpRequest();
        var countryName = component.get("v.countryName");
        xhttp.open("GET", "https://restcountries.eu/rest/v2/name/"+countryName, false);
        xhttp.send(null);
        if (xhttp.status === 200) {
            component.set("v.response", JSON.stringify(xhttp.responseText));
        }
        
    },
    
})

 

Usage Of lightning:availableForFlowScreens

Salesforce flow is one of the most powerful business automation tools that will be used to build the application with drag and drop. Now we will see how to use the lightning component available for the flow screens. Make your custom Lightning components available to flow screens in the Cloud Flow Designer by implementing the lightning:availableForFlowScreens interface. In the Cloud Flow Designer, flow screen components appear as options for Lightning component screen fields. In this example, I will get the current conversion rates from the external API and will display on the flow by using component.

Apex class
public class FlowGetExchangeRates {
    
    @AuraEnabled
    public static ConversionRates getConversionRates(String source , String target){
        ConversionRates cr = new ConversionRates() ;
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('http://apilayer.net/api/live?access_key=456fab5d3bee967f81169416e234387e&currencies='+target+'&source='+source+'&format=1');
        request.setMethod('GET');
        System.debug('response'+request);
        HttpResponse response = http.send(request);
        if (response.getStatusCode() == 200) {
            Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            System.debug('results'+results);
            Map<String , Object> qts = (Map<String , Object>) results.get('quotes');
            for(String s : qts.keySet() ){
                System.debug('s'+s);
                if(s=='USDEUR'){
                    cr.USDEUR =  String.valueOf((Object)qts.get(s));
                }
                if(s=='USDGBP'){
                    cr.USDGBP =  String.valueOf((Object)qts.get(s));
                }
                if(s=='USDCAD'){
                    cr.USDCAD =  String.valueOf((Object)qts.get(s));
                }
                if(s=='USDPLN'){
                    cr.USDPLN =  String.valueOf((Object)qts.get(s));
                }
            }
        }
        return cr ; 
        
    }
    
    
    public class ConversionRates{
        @AuraEnabled
        public String USDEUR{get;set;}
        @AuraEnabled
        public String USDGBP{get;set;}
        @AuraEnabled
        public String USDCAD{get;set;}
        @AuraEnabled
        public String USDPLN{get;set;}
        
        
    }
    
}

The above class is covert the exchange rates based on the source currency specified. Make sure you added the Rest API URI to remove site settings.

Lightning component 

<aura:component implements="lightning:availableForFlowScreens" access="global" Controller="FlowGetExchangeRates">
    <aura:attribute name="sourcecurrencies" type="String" access="global" default="USD"/>
    <aura:attribute name="targetcurrencies" type="String" access="global" default="EUR,GBP,CAD,PLN" />
    <aura:attribute name="resVal" type="FlowGetExchangeRates.ConversionRates" access="global"  />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_striped">
        <tr>
            <th>USD</th>
            <th>Conveted Val</th>
        </tr>
        <tr>
            <td>EUR</td>
            <td>{!v.resVal.USDEUR}</td>
        </tr>
        <tr>
            <td>GBP</td>
            <td>{!v.resVal.USDGBP}</td>
        </tr>
        <tr>
            <td>CAD</td>
            <td>{!v.resVal.USDCAD}</td>
        </tr>
        <tr>
            <td>PLN</td>
            <td>{!v.resVal.USDPLN}</td>
        </tr>
    </table>
    
    
</aura:component>

 

({
    doInit : function(component, event, helper) {
        var action=  component.get('c.getConversionRates'); 
        action.setParams({"source":component.get("v.sourcecurrencies") , "target":component.get("v.targetcurrencies")})
        action.setCallback(this,function(response){
            var state=response.getState();
            if(state=='SUCCESS')
            {
                component.set('v.resVal',response.getReturnValue()); 
            }
        });
        $A.enqueueAction(action);
    }
})

To make an attribute’s value customizable in the Cloud Flow Designer, add it to the component’s design resource. That way, flow admins can pass values between that attribute and the flow when they configure the Local Action element. That way, flow admins can pass values between that attribute and the flow when they configure the screen field. Here is the designer for component

<design:component>
    <design:attribute name="sourcecurrencies" label="sourcecurrencies"  default="USD"/>
    <design:attribute name="targetcurrencies" label="targetcurrencies"  default="EUR,GBP,CAD,PLN" />
</design:component>

A design resource describes the design-time behavior of a Lightning component information that visual tools need to allow adding the component to a page or app. Adding this resource is similar to adding it for the Lightning App Builder. When admins reference this component in a flow, they can pass data between the flow and the Lightning component. Use the Inputs tab to set an attribute using values from the flow. Use the Outputs tab to store an attribute’s value in a flow variable

Use Component in flow 

Now we will use the above lightning component in flow designer. Go to salesforce flow builder and create a new flow. Drag and Drop the screen element to flow designer and configure the flow screen as shown below. the flow contains the two input text element that will accept the source and target currencies.

 

Drag and drop another screen element into the designer and configure flow to receive the input values from the first screen element as shown below.

Click save. Connect the two flow screen elements as shown below. Set the first screen as a starting element

Testing 

Now go to the flow and enter the source and target currencies as shown below

Click Next and you will see the result from the Lightning component as shown below.

 

Custom Navigation Model for Flow Screens with Lightning Component

In this blog, I am going to explain how to use the custom lightning component button that is used to control the navigation of flow.By default, users navigate a flow by clicking standard buttons at the bottom of each screen. The lightning:availableForFlowScreens interface provides two attributes to help you fully customize your screen’s navigation. To figure out which navigation actions are available for the screen, loop through the availableActions attribute. To programmatically trigger one of those actions, call the navigateFlow action from your JavaScript controller.

Flow Navigation Actions

The availableActions attribute lists the valid navigation actions for that screen NEXT, BACK, PAUSE, RESUME, FINISH are the possible actions. A screen’s available actions are determined by:

  • Where in the flow the screen is. For example, Previous isn’t supported on the first screen in a flow, Finish is supported for only the last screen in a flow, and you can never have both Next and Finish.
  • Whether the flow creator opted to hide any of the actions in the screen’s Navigation Actions controls. For example, if Allow Pause is de-selected, the Pause action isn’t included in availableActions.

Component 

Here is the lightning component

<aura:component implements="lightning:availableForFlowScreens" access="global" >
    <!-- Get the script text from the flow -->    
    <aura:attribute name="scriptText" type="String" required="true" />
    <!-- Pass the value of the selected option back to the flow -->
    <aura:attribute name="value" type="String" />
    <aura:attribute name="action" type="String" />
    <div class="script-container">
        Navigation Action :   {!v.value}
    </div>
    <!-- Buttons for the agent to click, according to the customer’s response -->
    <div class="slds-p-top_large slds-p-bottom_large">
        <p><lightning:formattedText value="Customer Response" 
                                    class="slds-text-body_small" /></p>
        <lightning:buttongroup >
            <lightning:button label="Previous" aura:id="previousId" 
                              variant="neutral" onclick="{!c.handleChange}"/>
            <lightning:button label="Next" aura:id="nextId" 
                              variant="neutral" onclick="{!c.handleChange}"/>
            <lightning:button label="Finish" aura:id="finishId" 
                              variant="neutral" onclick="{!c.handleChange}"/>
            
        </lightning:buttongroup>
    </div>
</aura:component>

 

({
    handleChange : function(component, event, helper) {
        // When an option is selected, navigate to the next screen
        var response = event.getSource().getLocalId();
        component.set("v.value", response);
        var navigate = component.get("v.navigateFlow");
        console.log('navigate'+navigate)
        if(response=='previousId'){
            navigate("BACK");
        }
        if(response=='nextId'){
            navigate("NEXT");
        }
        if(response=='finishId'){
            navigate("FINISH");
        }  
    }
})

 

<design:component>
    <design:attribute name="scriptText" label="Script Text" 
                      description="What the agent should say to the customer" />
    <design:attribute name="action" label="Navigation Action" 
                      description="" />
        
</design:component>

Flow 

Here the simple flow that I created for testing
You can see below the image that shows how the lightning component interacts with flow navigation.
Once you click the next button on the component it will take to the next action i.e Finish.

Dynamically Update a Flow Screen with a Lightning Component

In this post, I am going to explain how to dynamically update the flow screen with the lightning component. To conditionally display a field on your screen, build a Lightning component that uses aura: if to check when parts of the component should appear. This component displays a custom script component and a group of radio buttons. The component gets the user feedback based on user-selected yes or no options.

If the user selects the Yes radio button, the component displays  form for the user get in touch details

If the user selects the Yes radio button, the component displays thanks message.

Here is the lightning component

<aura:component access="global" implements="lightning:availableForFlowScreens">
    
    <aura:attribute name="radioOptions" type="List" default="[
                                                             {'label': 'Yes', 'value': 'false'},
                                                             {'label': 'No', 'value': 'true'} ]"/>
    <aura:attribute name="radioValue" type="Boolean" default="true"/>
    <div class="survey-container">
        <div class="slds-card__header slds-grid slds-p-bottom_small slds-m-bottom_none">
            <div class="slds-media slds-media_center slds-has-flexi-truncate" >
                <div class="slds-media__figure slds-align-top">
                    <h2><lightning:icon iconName="utility:quotation_marks"
                                        title="Get in touch Details" /></h2>
                </div>
                <div class="slds-media__body">
                    <ui:outputRichText class="script" value="{!'Would you like to Get in touch with Sales?'}"/>
                </div>
            </div>
        </div>
    </div>
    
    <div class="slds-p-top_medium slds-p-bottom_medium">
        <lightning:radioGroup aura:id="rbg_correct" name="rbg_correct"
                              label="Would you like to take survey?"
                              options="{! v.radioOptions }" value="{! v.radioValue }" />
        <aura:if isTrue="{!v.radioValue}">
            <div class="slds-media__body">
                <ui:outputRichText class="script" value="{!'Thanks for your Time'}"/>
            </div>
        </aura:if>
        
        <aura:if isTrue="{!v.radioValue}">
            <aura:set attribute="else">
                <lightning:input label="Name" name="Name" class="slds-p-top_small" />
                <lightning:input type="tel"  label="Phone" class="slds-p-top_small"/>
                <lightning:input type="date" label="Birthday" name="date" class="slds-p-top_small"/>
                <lightning:input type="email" label="Email" name="email" value="abc@domain.com" class="slds-p-top_small"/>
            </aura:set>
            
        </aura:if>
        
    </div>
    
    
</aura:component>

Here is the flow which created for this example 

Below image shows how to invoke the lightning component in flow designer.