SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Tuesday 25 October 2022

How to pass data from lwc to screen flow in salesforce?

Hello Trailblazers,


In the previous post, we learned: How to pass data from screen flow to lwc in salesforce? In this post we're going to learn, how we can pass data from lightning web component (LWC) to the parent screen flow in salesforce. Let's begin by creating our LWC first.


LWC: createLead

This LWC will be used to create an instance of lead with some basic information (first name, last name, company) and pass it to the flow, it won't insert the lead record in salesforce. Once the lead record is received by the flow, we'll populate some other fields with default values like: Lead Source and Rating and then create the lead record from the flow itself. Let's Begin!

createLead.html

We have a simple lightning-card element and 2 lightning-input-field elements (to store first and last name). We also have 1 lightning-combobox element (to store company) as shown below:
<template>
    <lightning-card title={title}>
        <lightning-layout multiple-rows="true">
            <lightning-layout-item size="12" padding="around-small">
                <lightning-input type="text" name="FirstName" label="First Name" required></lightning-input>
            </lightning-layout-item>
            <lightning-layout-item size="12" padding="around-small">
                <lightning-input type="text" name="LastName" label="Last Name" required></lightning-input>
            </lightning-layout-item>
            <lightning-layout-item size="12" padding="around-small">
                <lightning-combobox label="Company" name="Company" placeholder="Select Company" options={companies} required></lightning-combobox>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>
Notice that all the fields in our form, are marked as requied. We'll pass the title of lightning-card card from our screen flow which will be stored in title property in js and is referenced here in html by the title attribute as title={title}. Let's see the JavaScript code now:

createLead.js

Have a look at the code snippet below. The default value for our title property is Create Lead. We also have another public attribute named leadRecord, this attribute will be passed to our screen flow with all the lead details. We have a companies array in our code which is used by lightning-combobox, so that the user can select a company for the lead. It's a text field in salesforce but let's consider hypothetically that this companies array is populated by doing a callout to an external system from LWC. We don't want our users to write any company name for our lead record, therefore, the flow to create a lead is as follows:

Screen flow launched -> LWC  component rendered inside screen flow -> Get valid company names by performing a callout to an external system from LWC -> User enter the first name, last name, select a company and click next -> Lead record passed from LWC to screen flow -> Screen flow add some default values for other fields -> Screen flow creates a new lead record in salesforce
import { api, LightningElement } from 'lwc';

export default class CreateLead extends LightningElement {

    @api title = 'Create Lead';
    @api leadRecord = {};

    companies = [
        {
            "label": "Apple",
            "value": "Apple"
        },
        {
            "label": "Saudi Aramco",
            "value": "Saudi Aramco"
        },
        {
            "label": "Microsoft",
            "value": "Microsoft"
        },
        {
            "label": "Alphabet (Google)",
            "value": "Alphabet (Google)"
        },
        {
            "label": "Amazon",
            "value": "Amazon"
        },
        {
            "label": "Tesla",
            "value": "Tesla"
        },
        {
            "label": "Berkshire Hathaway",
            "value": "Berkshire Hathaway"
        },
        {
            "label": "UnitedHealth",
            "value": "UnitedHealth"
        },
        {
            "label": "Johnson & Johnson",
            "value": "Johnson & Johnson"
        },
        {
            "label": "Exxon Mobil",
            "value": "Exxon Mobil"
        },
        {
            "label": "Visa",
            "value": "Visa"
        },
        {
            "label": "Walmart",
            "value": "Walmart"
        },
        {
            "label": "JPMorgan Chase",
            "value": "JPMorgan Chase"
        },
        {
            "label": "Meta Platforms (Facebook)",
            "value": "Meta Platforms (Facebook)"
        },
        {
            "label": "Chevron",
            "value": "Chevron"
        },
        {
            "label": "TSMC",
            "value": "TSMC"
        },
        {
            "label": "Eli Lilly",
            "value": "Eli Lilly"
        },
        {
            "label": "LVMH",
            "value": "LVMH"
        },
        {
            "label": "NVIDIA",
            "value": "NVIDIA"
        },
        {
            "label": "Procter & Gamble",
            "value": "Procter & Gamble"
        },
        {
            "label": "Mastercard",
            "value": "Mastercard"
        },
        {
            "label": "Nestlé",
            "value": "Nestlé"
        },
        {
            "label": "Tencent",
            "value": "Tencent"
        },
        {
            "label": "Home Depot",
            "value": "Home Depot"
        },
        {
            "label": "Bank of America",
            "value": "Bank of America"
        },
        {
            "label": "Samsung",
            "value": "Samsung"
        },
        {
            "label": "Roche",
            "value": "Roche"
        },
        {
            "label": "Kweichow Moutai",
            "value": "Kweichow Moutai"
        },
        {
            "label": "AbbVie",
            "value": "AbbVie"
        },
        {
            "label": "Pfizer",
            "value": "Pfizer"
        },
        {
            "label": "Merck",
            "value": "Merck"
        },
        {
            "label": "Coca-Cola",
            "value": "Coca-Cola"
        },
        {
            "label": "Pepsico",
            "value": "Pepsico"
        },
        {
            "label": "Novo Nordisk",
            "value": "Novo Nordisk"
        },
        {
            "label": "Costco",
            "value": "Costco"
        },
        {
            "label": "Reliance Industries",
            "value": "Reliance Industries"
        },
        {
            "label": "ICBC",
            "value": "ICBC"
        },
        {
            "label": "Oracle",
            "value": "Oracle"
        },
        {
            "label": "Thermo Fisher Scientific",
            "value": "Thermo Fisher Scientific"
        },
        {
            "label": "Alibaba",
            "value": "Alibaba"
        },
        {
            "label": "Shell",
            "value": "Shell"
        },
        {
            "label": "McDonald",
            "value": "McDonald"
        },
        {
            "label": "Walt Disney",
            "value": "Walt Disney"
        },
        {
            "label": "ASML",
            "value": "ASML"
        },
        {
            "label": "Toyota",
            "value": "Toyota"
        },
        {
            "label": "Broadcom",
            "value": "Broadcom"
        },
        {
            "label": "Danaher",
            "value": "Danaher"
        },
        {
            "label": "Cisco",
            "value": "Cisco"
        },
        {
            "label": "T-Mobile US",
            "value": "T-Mobile US"
        },
        {
            "label": "Astrazeneca",
            "value": "Astrazeneca"
        },
        {
            "label": "Wells Fargo",
            "value": "Wells Fargo"
        },
        {
            "label": "Accenture",
            "value": "Accenture"
        },
        {
            "label": "Novartis",
            "value": "Novartis"
        },
        {
            "label": "Abbott Laboratories",
            "value": "Abbott Laboratories"
        },
        {
            "label": "L'Oréal",
            "value": "L'Oréal"
        },
        {
            "label": "Salesforce",
            "value": "Salesforce"
        },
        {
            "label": "ConocoPhillips",
            "value": "ConocoPhillips"
        },
        {
            "label": "Bristol-Myers Squibb",
            "value": "Bristol-Myers Squibb"
        },
        {
            "label": "Verizon",
            "value": "Verizon"
        },
        {
            "label": "China Construction Bank",
            "value": "China Construction Bank"
        },
        {
            "label": "Texas Instruments",
            "value": "Texas Instruments"
        },
        {
            "label": "Linde",
            "value": "Linde"
        },
        {
            "label": "United Parcel Service",
            "value": "United Parcel Service"
        },
        {
            "label": "Adobe",
            "value": "Adobe"
        },
        {
            "label": "Nextera Energy",
            "value": "Nextera Energy"
        },
        {
            "label": "China Mobile",
            "value": "China Mobile"
        },
        {
            "label": "Tata Consultancy Services",
            "value": "Tata Consultancy Services"
        },
        {
            "label": "Nike",
            "value": "Nike"
        },
        {
            "label": "CATL",
            "value": "CATL"
        },
        {
            "label": "Agricultural Bank of China",
            "value": "Agricultural Bank of China"
        },
        {
            "label": "Amgen",
            "value": "Amgen"
        },
        {
            "label": "Comcast",
            "value": "Comcast"
        },
        {
            "label": "Morgan Stanley",
            "value": "Morgan Stanley"
        },
        {
            "label": "Hermès",
            "value": "Hermès"
        },
        {
            "label": "Philip Morris",
            "value": "Philip Morris"
        },
        {
            "label": "TotalEnergies",
            "value": "TotalEnergies"
        },
        {
            "label": "Charles Schwab",
            "value": "Charles Schwab"
        },
        {
            "label": "Raytheon Technologies",
            "value": "Raytheon Technologies"
        },
        {
            "label": "QUALCOMM",
            "value": "QUALCOMM"
        },
        {
            "label": "Netflix",
            "value": "Netflix"
        },
        {
            "label": "BHP Group",
            "value": "BHP Group"
        },
        {
            "label": "Royal Bank Of Canada",
            "value": "Royal Bank Of Canada"
        },
        {
            "label": "PetroChina",
            "value": "PetroChina"
        },
        {
            "label": "Honeywell",
            "value": "Honeywell"
        },
        {
            "label": "Elevance Health",
            "value": "Elevance Health"
        },
        {
            "label": "AT&T",
            "value": "AT&T"
        },
        {
            "label": "CVS Health",
            "value": "CVS Health"
        },
        {
            "label": "Lockheed Martin",
            "value": "Lockheed Martin"
        },
        {
            "label": "Intuit",
            "value": "Intuit"
        },
        {
            "label": "Union Pacific Corporation",
            "value": "Union Pacific Corporation"
        },
        {
            "label": "Bank of China",
            "value": "Bank of China"
        },
        {
            "label": "IBM",
            "value": "IBM"
        },
        {
            "label": "Deere & Company",
            "value": "Deere & Company"
        },
        {
            "label": "Toronto Dominion Bank",
            "value": "Toronto Dominion Bank"
        },
        {
            "label": "Lowe's Companies",
            "value": "Lowe's Companies"
        },
        {
            "label": "Unilever",
            "value": "Unilever"
        },
        {
            "label": "HDFC Bank",
            "value": "HDFC Bank"
        },
        {
            "label": "Goldman Sachs",
            "value": "Goldman Sachs"
        },
        {
            "label": "Intel",
            "value": "Intel"
        },
        {
            "label": "Medtronic",
            "value": "Medtronic"
        }
    ];

    @api
    validate() {
        const inputFields = this.template.querySelectorAll('lightning-input');
        const comboBox = this.template.querySelector('lightning-combobox');
        const validity = {
            isValid: true,
            errorMessage: 'Please fill the required fields!'
        };
        inputFields.forEach(inputField => {
            if(inputField.checkValidity()) {
                this.leadRecord[inputField.name] = inputField.value;
            } else {
                validity.isValid = false;
            }
        });
        if(comboBox.checkValidity()) {
            this.leadRecord[comboBox.name] = comboBox.value;
        } else {
            validity.isValid = false;
        }
        return validity;
    }
}
We're not actually performing the callout here as our main task is to focus on data validation and pass data from lwc to screen flow in salesforce but you can think of it as a requirement and the solution which we've implemented for the same.

validate()

Let's discuss about our validate() method now. As you can see in the above code snippet, it's a public method with @api annotation. This method will be called automatically when the user clicks on Next button of our screen flow. You can perform any kind of validation here, for now, we'll just see if all the fields are filled or not. Another important part we're doing here is: We'll populate the leadRecord property which will then be passed to our screen flow.

If you see in the above code snippet, first of all, we got all the lightning input fields using this.template.querySelectorAll('lightning-input') and stored it in inputFields constant. Similarly, we queried the lightning combobox field using this.template.querySelector('lightning-combobox') and stored it in comboBox constant. Finally, we initialized a constant named validity with an object which has two properties isValid and errorMessage. This object will be returned by our validate method and can notify the flow that there's an error in LWC and the flow should not proceed ahead. We've initialized isValid to true, however it should be false if there is a validation error and the errorMessage that we provide here will be used by the flow to show a validation error on the flow screen. For our component, we've the error message as Please fill the required fields! as we'll show this error only if any of the required fields is not filled.

Next we've iterated over all the input fields using a forEach loop and we've checked validity of each input field using the checkValidity() method. This method will return true if the input field is valid, otherwise, it'll return false

If the input field is valid, we're populating the lead record's property with the correct value as: this.leadRecord[inputField.name] = inputField.value. Notice by going back to the HTML section that each input field has a name attribute associated with it and the value of that name attribute is nothing but the API name of the actual salesforce field like: FirstName, LastName, Company. Therefore, here we're populating leadRecord['FirstName'] = <value of FirstName field> and leadRecord['LastName'] = <value of LastName field>.

If the input field is not valid, we've setup isValid property of validity to false as: validity.isValid = false

A similar check is added for our lightning-combobox input as well. Finally, we returned our validity object. If you notice, we've done two things here:

  1. Validated the input in our lwc using the validate() method and returned the proper output.

  2. Populated the leadRecord object which will be passed to our screen flow

Out JavaScript part is complete here. Let's move on to the meta file now:

createLead.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__FlowScreen</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__FlowScreen">
            <property name="title" type="String" label="Title" role="inputOnly"></property>
            <property name="leadRecord" type="@salesforce/schema/Lead" label="Lead Record" role="outputOnly"></property>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>
As you can see above, this is very much similar to the meta file that we added in our previous tutorial. We've added a target named lightning__FlowScreen so that the component can be used in flows. Under the configurations for this target, we have two properties:

  1. title: The title of our lightning-card and will be passed by our flow to lwc. Notice that it's of type String. The label is Title which is fine and role is inputOnly as we just want to get this variable’s value from the screen flow as an input, we don't want to pass it's value back to the flow if it's updated in our lwc.

  2. leadRecord: The type of this property is @salesforce/schema/Lead because it'll store the actual salesforce lead record as per the inputs by the user. The label is Lead Record and the role for this property is outputOnly. This is because we'll be passing the value of this variable from LWC to flow. We don't want the admin to set it's value from flow and pass it to LWC.

So, that's all for our LWC. Let's move on to the screen flow now!

Screen Flow: Create Lead

Our screen flow looks like this:

Variable: LeadRecord

First of all, let's create a variable named LeadRecord. It should be of data type Record and object name should be Lead as shown below:
This variable will be mapped to the leadRecord variable of our LWC. Click on Done

Screen: Populate Lead Record

In order to use it, let's configure our Populate Lead Record screen. This is where we'll embed our createLead LWC. Let's see what do we have in this:
As you can see above, on the left hand side, we can search for createLead lwc and it'll be available under the Custom section (because we've added lightning__FlowScreen in our LWC meta file). We can drag and drop this on our screen shown in the middle within our screenshot above. On the right hand side, we can specify the API name for this component like: CreateLead and the Title. This is the same title property that we marked as inputOnly in our targetConfig. This title will be displayed on our lightning-card. The title we entered here is: Let's create a new Lead

Now, it's time to configure our leadRecord property of LWC's targetConfig. Under the Advanced settings, click Manually assign variables and you'll find an option to assign Lead Record property of LWC to a flow variable as shown below:
As you can see above, we can choose our LeadRecord variable here that we created initially. This is where we're passing the output from our LWC to a screen flow variable. Isn't it so simple?

Make sure to populate Screen Properties with value Populate Lead Record as shown below:
Click on Done

Assignment: Assign Default Values

Now we have the lead record coming from LWC with FirstName, LastName and Company populated. Let's assign default values to our LeadSource and Rating as well using the Assignment component, as shown below:
Click on Done

Create Records: Create Lead

Finally, we'll use the Create Records component to create a new lead record using our LeadRecord variable as shown below:

Click on Done. Now, it's time to save our new flow, let's name it as Create Lead:

Click on Save. Make sure to activate it as well, so the highlighted button below is showing Deactivate:
Our flow is complete!

Add Screen Flow to Homepage

Now it's time to test our flow. Go to any lightning app (I am using the Sales app here). Click on the Edit Page section:
On the lightning page builder, follow the steps as shown below:
  1. Search for flow in the left sidebar under Components

  2. Drag and Drop the flow component on the lightning page

  3. Choose the Create Lead flow from the Flow Configurations

  4. Save the page

  5. Activate the page (if not done before) and set it as org default as we did in our previous tutorial.

Once the flow is ready, you can populate the information of the lead as shown below:
As you click on Next, a new lead will be created in the system. You can check the Lead record and it'll have all the information populated as shown below:
As you can see above, we have the Name (FirstName + LastName), Company, Lead Source and Rating populated as expected.

In case you don't fill any fields in our screen flow and click Next, you'll have the Error Message visible on the screen as shown below:
This error message is coming from our validate() method of LWC and is displayed by the flow automatically.

That's all for this tutorial. I hope you liked it. Let me know your feedback in the comments down below.

Happy Trailblazing!!

No comments:

Post a Comment