Thursday 28 April 2022

Lightning Web Component - Display value of lookup(parent) records using Lightning Data Table

SCENARIO :
While working on one of the requirements for a health sector project for a client based out of MelbourneAustralia there was a requirement to display records with its lookup object fields in the lightning data table.

APPROACH : 
As the requirement is pretty straightforward, The client wants an LWC to display contact records with its related Account record(Parent) value on Lightning Data Table. 

Salesforce standard Lightning Data table is the most excellent table that is ready to utilize to show the record with interactive UI.

There are some limitations to using the standard Lightning Data table like we can not have a lookup field to directly resemble to display in the column.

In this blog, We will see how we can display Lookup fields in Lightning Data Table.

Let's jump with the example, Here we will display the Contact object records with the lookup object field Account Name.  

DataTableLwcController.cls
public without sharing class DataTableLwcController {
    
    @AuraEnabled(cacheable=true)
    public static list<Contact> getRecords(){
        return [SELECT Id,Name,AccountId,Account.Name,Phone,Email 
                       FROM contact WHERE AccountId != null LIMIT 10 ];
    }
        
}

The response that we will get from the apex method is a nested object. The response looks as below.

{
    "Id": "0032x000004N3PxAAK",
    "Name": "Rose Gonzalez",
    "Phone": "(512) 757-6000",
    "Email": "rose@edge.com",
    "AccountId": "0012x000006F1iAAAS",
    "Account": {
      "Name": "Edge Communications",
      "Id": "0012x000006F1iAAAS"
    }
}

To display lookup object fields, we need to resemble the response in the same object as below.

{
    "Id": "0032x000004N3PxAAK",
    "Name": "Rose Gonzalez",
    "Phone": "(512) 757-6000",
    "Email": "rose@edge.com",
    "AccountId": "0012x000006F1iAAAS",
    "AccountName": "Edge Communications",
    "ContactLink":"/0032x000004N3PxAAK",
    "AccountLink":"/0012x000006F1iAAAS",
    "Account": {
      "Name": "Edge Communications",
      "Id": "0012x000006F1iAAAS"
    }
}

Okay! Let's see in the js code how we can resemble the response.

let tempRecords = JSON.parse(JSON.stringify(data));
tempRecords = tempRecords.map(row => {
return { ...row,
         AccountName: row.Account.Name,                      
         ContactLink:'/'+row.Id,
         AccountLink:'/'+row.AccountId
       };

Below is our Lightning web Component

dataTableLwcExample.html
<template>
    <lightning-card title="Contacts" icon-name="standard:contact">
        <lightning-datatable 
            data={contacts} 
            columns={columns} 
            key-field="Id" 
            hide-checkbox-column='true' 
            show-row-number-column="true">
        </lightning-datatable>
    </lightning-card>
</template>

dataTableLwcExample.js
import { LightningElement , track , wire } from 'lwc';
import getRecords from '@salesforce/apex/DataTableLwcController.getRecords';

export default class DataTableLwcExample extends LightningElement {
    @track contacts;
    @track columns = [
        { 
            label: 'Name', 
            fieldName: 'ContactLink',
            type: 'url' ,
            typeAttributes: {label: { fieldName: 'Name' }, target: '_blank'}
        },
        { 
            label: 'Phone',
            fieldName: 'Phone',
            type: 'phone' 
        },
        { 
            label: 'Email',
            fieldName: 'Email',
            type: 'Email' 
        },
        { 
            label: 'Account',
            fieldName: 'AccountLink',
            type: 'url',
            typeAttributes: {label: { fieldName: 'AccountName' }, target: '_blank'},
            cellAttributes: { iconName: 'standard:account' } 
        }    
              
    ];
    //wiring an apex method to a function
    @wire(getRecords)
    WireAssignmentRecords({ data }) {
        if (data) {
            
            let tempRecords = JSON.parse(JSON.stringify(data));
            console.log(tempRecords);
            //binding the data into the same object
            tempRecords = tempRecords.map(row => {
                return { ...row,
                         AccountName: row.Account.Name,  
                         ContactLink:'/'+row.Id,         // Creating URL for Contact
                         AccountLink:'/'+row.AccountId   // Creating URL for Account
                       };
            }) 
            //Assign tempRecords to the contacts
            this.contacts = tempRecords;
            
        }
    }
}

Our Lightning Web Component looks like as below,

Output:-




If you have any questions you can reach out our Salesforce Consulting team here.

Thursday 21 April 2022

[Resolved:] Displaying and Redirecting Vf Page from Aura Component by disabling clickjack protection using iframe through quick action


SCENARIO 

While working on one of the requirements for an insurance sector solutions customer based in Chicago, Illinois there was a requirement to display and redirect Vf page from the lightning aura component through quick action by compulsorily using IFrame.

CHALLENGE

We achieved this functionality of redirecting VF page from aura component.

However, we were not able to display VF page from aura component.  

APPROACH

So in order to solve the issue of displaying Vf page from aura component, we have  to Disable Clickjack protection whenever we are using IFrame

Why and When to use IFrame?

  • An IFrame can insert all sorts of media. It allows you to add documents, videos, and interactive media within a page. By doing this, you can display a secondary webpage on your main page. It also allows you to include a piece of content from other sources. It can integrate the content anywhere within your page, without having to include them in your web layout’s structure. 

  • Developers mainly use the IFrame tag to embed(place content) HTML document within the current one, it lets you add an independent HTML document with its browsing context. It is useful for pulling content from other domains, it safely allows the advantage of being able to visually show data from other domains without letting them stomp all over your page with unlimited access. 

EXAMPLE CODE:

Redirect.vfp:

<apex:page showHeader="false" sidebar="false" controller="RedirectExample">
<apex:form >
<apex:pageblock >
<apex:commandlink action="{!redirect}" target="_blank">

<apex:commandButton value="Open in New Tab" />
</apex:commandLink>
</apex:pageblock>
</apex:form>
</apex:page>

RedirectExample.apxc:

public class RedirectExample {
public RedirectExample(){

}
public pageReference redirect() {
PageReference pageRef = new PageReference('http://www.google.com');
pageRef.setRedirect(true);
return pageRef;
}
}

vfpinsidelightning.cmp:

<aura:component implements="forceCommunity:availableForAllPageTypes,force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" >
    <iframe src="Enter your namespace here.com/apex/Redirect?core.apexpages.request.devconsole=1"/>
</aura:component>


Create quick action as shown below:


 

Now add this newly created quick action on your objects page layout

In 'Mobile & Lightning Action' section drag your quick action from the options provided and add it below 'Salesforce Mobile and Lightning Experience Action' .


In order to display vf page in the aura component using iframe, we have to follow the steps as mentioned below:
    
  • Go to Setup.
  • Write Session settings in quick find  search box .
  • Select Session Settings.
  • Uncheck ”Enable clickjack protection for customer Visualforce pages with headers disabled” under Clickjack Protection section.


OUTPUT:





CONCLUSION

By following above-mentioned steps and given approach, We were able to display vf page in aura component by disabling Clickjack Protection via quick action using an IFrame.




If you have any questions you can reach out our Salesforce Consulting team here.

Thursday 14 April 2022

Lightning Web Component in Flows

Lightning Flow Builder is one of the powerful automation tools available in Salesforce which gets the work done without a single line of code.

In Lightning Flow Builder, you can accomplish almost anything like creating records, updating records, sending emails, triggering approval processes, connecting to an external system, and from winter 20 release, Lightning Web Components can be incorporated into Flow.

Lightning Web Components in Flow: Why to use them
Lighting Web Components in Flow provides a better User experience and also accomplishes many tasks that flow cannot be accomplished alone.

Adding Lightning Web Components to Flows
To add Lightning Web Components available for Flow screens, add a target to the targets tag in the component’s Meta XML file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__FlowScreen</target>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
        
</LightningComponentBundle>

By adding the above tags, the Lightning Web Component becomes available for flow screens.

Let's have a look at the example, A flow that displays a list of account with Lightning Design, select the accounts, and display the selected account Ids.

Wait a while!. How would the Flow knows which accounts are selected in the Lightning component ?, Is there any mechanism to communicate between LWC and Flows? We must declare the properties that can communicate between LWC and Flow. Since we have Targets, we also have Target Configs. To look more at the Configuration tag in Salesforce Documentation.

Let's Jump into the Lightning Web Component Code.

Lightning Web Component

lwcFlow.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
    <lightning-card title="Accounts List" icon-name="standard:account">
        <div class="slds-p-around_x-small">
            <table class="slds-table slds-table_cell-buffer slds-table_bordered">
                <thead>
                    <tr class="slds-line-height_reset">
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Edit">Select</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Account Name">Account Name</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Industry">Industry</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Phone">Phone</div>
                        </th>                        
                    </tr>
                </thead>
                <tbody>
                    <template for:each={accs} for:item="acc"> 
                        <tr key={acc.Id} class="slds-hint-parent">
                            <td data-label="Prospecting">
                                <div class="slds-truncate" title="Select">
                                    <lightning-input type="checkbox-button" label="select" variant="label-hidden" onchange={handleCheck} name={acc.Id}></lightning-input>
                                </div>
                            </td>                                
                            <td data-label="Prospecting">
                                <div class="slds-truncate" title="Account Name">{acc.Name}</div>
                            </td>
                            <td data-label="Confidence">
                                <div class="slds-truncate" title="Industry">{acc.Industry}</div>
                            </td>
                            <td data-label="Confidence">
                                <div class="slds-truncate" title="Phone">{acc.Phone}</div>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>
        </div>
    </lightning-card>
</template>

lwcFlow.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { LightningElement,api } from 'lwc';

export default class LwcFlow extends LightningElement {
    @api selectedAccs = [];
    @api selectedAccsString;
    @api accs = [];

    handleCheck(event) {
        if(!this.selectedAccs.includes(event.currentTarget.name))
            this.selectedAccs.push(event.currentTarget.name);
        else {
            for(let i = 0; i < this.selectedAccs.length; i++) {
                if(event.currentTarget.name === this.selectedAccs[i])
                this.selectedAccs.splice(i, 1);
            }
        }
        
        // eslint-disable-next-line @lwc/lwc/no-api-reassignments
        this.selectedAccsString = JSON.stringify(this.selectedAccs);
        
    }
}
 
lwcFlow.js-meta.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__FlowScreen</target>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>

    <targetConfigs>
        <targetConfig targets="lightning__FlowScreen">
            <property name="accs" type="@salesforce/schema/Account[]" label="Accounts" description="list of Accounts"/>
            <property name="selectedAccs" type="String[]" label="Selected Accounts" description="Selected Account Ids"/>
            <property name="selectedAccsString" type="String" label="Selected Accounts String" description="Selected Account Ids"/>
        </targetConfig>
    </targetConfigs>    
</LightningComponentBundle>

Lightning Web Component looks good. Now, Let's build Screen Flow.

Flow
Step 1:-  Define flow properties.
  • Click Setup.
  • In the Quick Find box, type Flows.
  • Select Flows then click on the New Flow.
  • Select the Screen Flow option and click on Next and configure the flow as follows:
    • How do you want to start building: Freeform
Step 2:- Flow variables
In this step, we will define variables.

Define AccountList  collection to store Account records.
  • Under Toolbox, select Manager, click on New Resource.
  • Input following information,
    • Resource Type - Variable
    • API Name - AccountList
    • Data Type - Record
    • Object - Account
    • Allow multiple values (collection) - Check 
    • Availability Outside the Flow
      • Available for input - Check
      • Available for output - Check
  • Click Done.
AccountList Collection

Define SelectedIds Text variable to store Account Ids as string.
  • Under Toolbox, select Manager, click on New Resource.
  • Input following information,
    • Resource Type - Variable
    • API Name - SelectedIds
    • Data Type - Text
    • Allow multiple values (collection) - Uncheck 
    • Availability Outside the Flow
      • Available for input - UnCheck
      • Available for output - Check
  • Click Done.
SelectedIds Variable

Step 3:-  Create Get Records Element
In this step, we will create Get Records Element to get Account records.
  • Under Toolbox, select Elements. Drag and drop onto Get Records Element the canvas.
  • Fill following Information.
    • Enter Label the API Name will auto-populate.
    • Object - Account
    • Filter Account Records
      •  Condition Requirements -All Conditions Are Met (AND)
      • Industry Equals - Electronics
    • Sort Account Records - Ascending
    • Sort By - Name
    • How Many Records to Store - All records
    • How to Store Record Data - Choose fields and assign variables (advanced)
    • Select Variable to Store Account Records
      • Record Collection - AccountList
    • Select Account Fields to Store in Variable
      • ID
      • Name
      • Industry
      • Phone
  • Click Done.
  • Connect Start to Get Records Element.

Get Records Element

Step 4:-  Create Screen Element
In this step, we will create screen element for Lightning Web Component .
  • Under Toolbox, select Elements. Drag and drop Screen onto the canvas.
  • Fill following Information on Screen Properties.
    • Enter Label the API Name will auto-populate.
    • Under Components, select Custom, Drag and drop lwcFlow onto the screenfill the following details on lwcFlow.
      • Label - Display Accounts
      • API Name - Display_Accounts
      • Accounts- {!AccountList}
      • Selected Accounts String - {!SelectedIds}
      • Advanced 
      • Manually assign variables - Check
      • Store Output Values to Select Variables
        • Accounts - {!AccountList}
        • Selected Accounts String - {!SelectedIds}
      • Revisited Screen Values
        • Use values from when the user last visited this screen - Check
  • Click Done.
  • Connect Get Records Element to Screen Element.



Screen Element
Step 5:-  Create Screen Element
In this step, we will create screen element to display selected Account Ids.
  • Under Toolbox, select Elements. Drag and drop Screen onto the canvas.
  • Fill following Information on Screen Properties.
    • Enter Label the API Name will auto-populate.
    • Under Components, select Display, Drag and drop Display Text onto the screenfill the following details on Long Text.
      • API Name - displayIds
      • Insert a resource ... - {!SelectedIds} 
  • Click Done.
  • Connect Screen Element to Screen Element.
Screen Element

Flow Diagram 

Output 
If you have any questions you can reach out our Salesforce Consulting team here.