Flow Invocable Actions

In this post, I will show how to use the flow Invocable Action… Actions are easy to discover and use, and also easy to understand and implement. Every button and link in Salesforce can be considered an action. A consistent Actions API and framework support the creation and distributed use of actions throughout Salesforce. Actions are available in the REST API. Invocable actions, also known as dynamic actions, can be invoked from a common endpoint in the REST API. Here we will be creating a flow and calling the flow using the REST API. The example we are taking here is returning the order and order lines items based on the order name from the auto-launched flow.

1. Create a flow 

now create a flow that will take the input argument from the rest API and return query order and order line items based on the order name.now from the flow designer create a new input variable that will collect the order name.

 

Drag and drop the fast lookup on the flow builder and Lookup the order name by passing the Order name from the inputVariable created above

OrderResult is sobject variable and adds the following fields

Now drag and drop one more Fast lookup now to query the order line items and this time we will assign the value to result variable to sobject collections.

Orderlines in the sobject collection type and return the following fields.

Connect the two fast lookups as shown below

Now save the flow of type Auto launched Flow as shown below

2. Calling Flow Actions

Now you will be able to call the flow from the invocable flow actions. Now you need to use the custom flow actions rest URI

/vXX.X/actions/custom/flow

Invokes the OrderAPIService flow

/vXX.X/actions/custom/flow/OrderAPIService

You will be able to execute the REST API from the workbench. Go to the salesforce workbench and execute the rest API as shown below

After executing the flow action, you can able to see the result as shown below

Understanding Input and Output

        Input values vary according to the input variables specified for each flow. For autolaunched flows, the input values vary according to the input variables in that flow. In this example the input variable is OrderNumber Invocable processes always require either one of the following input parameters:

  • sObjectId: The Id of the sObject record that you want the process to execute on. The record must be of the same object type as the one on which the process is defined.
  • sObject: The sObject itself that you want the process to execute on. The sObject must be of the same object type as the one on which the process is defined.
{
   "inputs":[
      {
         "OrderNumber":"00000100"
      }
   ]
}

Output values vary according to the output variables specified
Flow__InterviewStatus is available for flows. Invocable processes do not have outputs. In the response, you can able to see the Flow__InterviewStatus as finished.

 

Lightning web components Data Binding in a Template

Let us discuss here how using the Data Binding in a Template. Templating allows binding values from the JavaScript controller value to the Html view in Lightning web component. In Lightning web component, this can be achieved using simple {property} bindings. Bind properties in a component’s template to properties in the component’s JavaScript class. In the template, surround the property with curly braces, {property}. To compute a value for the property, use a JavaScript getter in the JavaScript class, get property(){}. Don’t write JavaScript expressions in a component’s template. Refer to this link for how to set up the environment.

Using Expressions

Here’s the simplest example of data binding using an expression. The greeting property in the template is bound to the greeting property in the JavaScript class.

<!-- hello.html -->
<template>
    Hello, {greeting}!
</template>
// hello.js
import { LightningElement } from 'lwc';

export default class Hello extends LightningElement {
    greeting = 'World';
}

Now in the above example, the greeting property is blinded from the controller using the {greeting} binding expression to UI. The property in { } must be a valid JavaScript identifier or member expression.

Now its understand how to bind the expression from the input value instead of a hard-coded string.update the markup as shown below

<template>
    <p>Hello, {greeting}!</p>
    <lightning-input label="Name" value={greeting} onchange={handleChange}></lightning-input>
</template>

update the JavaScript controller as shown below.

import { LightningElement, track } from 'lwc';

export default class HelloBinding extends LightningElement {
    @track greeting = 'World';

    handleChange(event) {
        this.greeting = event.target.value;
    }
}

If you look at the above JavaScript controller we are using the event handling in the JavaScript class will pass the event to handleChange method. The event object contains information about the change event. The component uses the event object to get the value that the user entered into the input field. we are using @track decorator to track the greeting propriety. Here is the output

Using Getter 

We will be using javascript getter to compute a value for a property. For example, to convert the name to all uppercase letters, use a getter function in the JavaScript class, not an expression in the template. The getter is much more powerful than expressions because they’re JavaScript functions. Define the Getter as shown below syntax.

get propertyName() { ... }

Access the getter from the template using the below syntax

{propertyName}

In this example, a user enters their first and last name. A JavaScript getter computes a new value and updates the value to the upper case. Update the markup as shown below.

<template>
    <div class="slds-m-around_medium">
        <lightning-input name='firstName' label="First Name" onchange={handleChange}></lightning-input>
        <lightning-input name='lastName' label="Last Name" onchange={handleChange}></lightning-input>
        <p class="slds-m-top_medium">Uppercased Full Name: {uppercasedFullName}</p>
    </div>
</template>

The uppercasedFullName property in the template is bound to the get uppercasedFullName() getter in the JavaScript class. here is the JavaScript class

import { LightningElement, track } from 'lwc';

export default class HelloExpressions extends LightningElement {
    @track firstName = '';
    @track lastName = '';

    handleChange(event) {
        const field = event.target.name;
        if (field === 'firstName') {
            this.firstName = event.target.value;
        } else if (field === 'lastName') {
            this.lastName = event.target.value;
        }
    }

    get uppercasedFullName() {
        return `${this.firstName} ${this.lastName}`.toUpperCase();
    }
}

The handleChange function sets the firstName and lastName properties to the values that the user enters. The uppercasedFullName() getter combines and uppercases the names and update the value to UI. You can see in JavaScript class we are using @track decorator with firstName and lastName Because firstName and lastName decorated with @track, if the value of firstName or lastName changes, the template rerenders. If we remove @track, the property values still change, but the component doesn’t re-render, so we don’t see update values in UI. Decorating a property with @track makes it private and reactive. To mark a property public and reactive, use @api.Here is the final output.

Here is another example that will get the data from the contact record and shows the data.

<template>
        <lightning-card title="My Contact Record" icon-name="standard:contact">
            <template if:true={contact.data}>
                <div class="slds-m-around_medium">
                        <lightning-output-field value={firstName} name='firstName' label="First Name"></lightning-output-field>
                        <lightning-output-field value={lastName} name='lastName' label="Last Name" >
                        </lightning-output-field>
                        <p class="slds-m-top_medium">Uppercased Full Name: {uppercasedFullName}</p>
                </div>
            </template>
            
        </lightning-card>
</template>
import { LightningElement, api, wire ,track} from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
const fields = [
    'Contact.FirstName',
    'Contact.LastName',
];
export default class HelloExpressions extends LightningElement {
     @api recordId;
     @wire(getRecord, { recordId: '$recordId', fields })
     contact;
     get firstName() {
        return this.contact.data.fields.FirstName.value;
    }

    get lastName() {
        return this.contact.data.fields.LastName.value;
    }
    get uppercasedFullName() {
        return `${this.contact.data.fields.FirstName.value} ${this.contact.data.fields.LastName.value}`.toUpperCase();
    }
}

 

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));
        }
        
    },
    
})