Showing posts with label LWC. Show all posts
Showing posts with label LWC. Show all posts

Monday, February 26, 2024

Trailblazer Community Event: Kolkata Trailblazers Spring 24 Connect

 

Motivation behind this

 
2 Trailblazers community groups have successfully organized in-person event on 24th February, 2024 and it has a great time meeting with Kolkata Trailblazers. Being group leader I would convey my thanks to all of them.

Organizers:


Venue:

Webel IT Park Tower 1, BN4 , Salt Lake Sector V, Kolkata 700091

Attendees: 70+




Sessions:


1) GraphQL in LWC


Speaker: Anamika Mondal

This session covers:
  • Overview
  • REST vs GraphQL API
  • Live Demo
  • Benefits & Limitations
  • Q&A


2) Tips to pass Salesforce Data Cloud Consultant certification


Speaker: Santanu Boral


Glimpse of Event



Join with us at our Telegram Channel

Thank you for joining with us and receiving gifts for all.

Further Reading


Sunday, December 18, 2022

Trailblazer Community Session: Solving Common Use Cases using Einstein Bot

 

Motivation behind this


We, Kolkata Developer Group leaders have successfully organized virtual knowledgeable session on Einstein Bot. 

Name: Solving Common Use Cases using Einstein Bot

On: Saturday, December 17, 2022

Speaker: Arunangshu Pal and Santanu Boral

Attendees: 122

Check ins: 42

Organizer: Santanu Pal, Kolkata Developer Group Leader





Agenda


Following topics we have covered:

  • Configuring Einstein Bot
  • Transferring to Service Agent
  • Embedding LWC Component in Einstein Bot
  • Implementation of Confetti celebration in Einstein Bot
  • Implementation of navigation in Einstein Bot
By the end of this session, you will understand how to handle common use cases using Bot.

Link to the Deck: Session Deck

Recordings


Please find the recordings of the session.


Thank you for joining with us!

Sunday, December 11, 2022

My Dreamforce Session LWC 20-20: 20 Tips in 20 Minutes

 

Motivation behind this

 
Dreamforce 2022 is a memorable event where my dream to be a speaker becomes true. I have delivered my first session on LWC and I would like to share the glimpse of my session as well as session deck.
 

Session Details


In this developer-focused session, we’ll explore the potentials of Lightning Web Components (LWC). Learn 20 valuable tips in 20 minutes and start delivering better coding and best practices, including:   
  • UI Considerations 
  • Use of Reactives 
  • Design Considerations  
  • Communications and Event Propagations 
  • LWC Security

Attendees


175 trailblazers have swiped for this session.

Session Deck





Promotions from Salesforce



Agenda Builder


Link to Agenda Builder: LWC 20-20


Glimpse of my session





It's a great experience which I can't express.

As theater session is not getting recorded so I have delivered the same session with recording at ApexHours on 4th Jan 2023.

Session delivered at ApexHours





I am thankful to Amit Chaudhary for giving me opportunity to host same session at ApexHours.

Finally, thank you for reading!

Further Reading


Sunday, November 27, 2022

Trailblazer Community Event: Kolkata Trailblazers Connect Pre-winter 2022

 

Motivation behind this

 
2 Trailblazers community groups have successfully organized in-person event on 26th Nov,2022 and it has a great time meeting with Kolkata Trailblazers. Being group leader I would convey my thanks to all of them.

Organizers:


Venue:

Webel IT Park Tower 1, BN4 , Salt Lake Sector V, Kolkata 700091

Attendees: 30




Sessions:


1) LWC 20-20: 20 Tips in 20 Minutes


Speaker: Santanu Boral

In this developer-focused session, we’ll explore the potentials of Lightning Web Components (LWC). Learn 20 valuable tips in 20 minutes and start delivering better coding and best practices, including:

  • UI Considerations
  • Use of Reactives
  • Design Considerations
  • Communications and Event Propagations
  • LWC Security
Same session has been delivered during Dreamforce 2022.





2) Managing Business Exceptions using Flows


Speaker: Santanu Pal

The session covers:
  • Basics about Exception and Error
  • Manage exception from Batch Class
  • Manage exception from Screen Flow
  • Manage exception from Auto Launched Flow
  • Adhering Best Practices to manage exceptions by Flow

By the end of this session, you will understand how to handle exceptional scenarios by Flow





Glimpse of Event



Join with us at our Telegram Channel

Thank you for joining with us!

Thursday, February 25, 2021

Lightning Message Service: Communication between Lightning Web Components

 

Motivation behind this


Earlier I have written a post on Drag and Drop functionality using Salesforce Lightning Web Components leveraging pub-sub event propagation which works on publish-subscribe model. 

With the introduction of Lightning Message Service (LMS) the communication between components can be possible either in single page or multiple pages over a message channel. It also works to communicate among Visualforce pages, Aura components, Lightning Web Components.

This flexible event communication mechanism drives me to do a proof-of-concept.

Lets get started.


Use Case


Business has a requirement to see a Company's location on google map when company is chosen.

Developer wants to build using Lightning Web Components where one component will list down the companies and other component will show the map. Both are unrelated but listen to the events based on subscription.

Possible End Results


After building the use case, it will perform the functionality as following video:


The screen will look like this:

LMS Demo



Solution Approach


To start with, first create a message channel and push it to the org. the xml will be placed under force-app/main/default/messageChannels/ directory.

Here message channel name has given as Selected_EntityId and the file name will be Selected_EntityId.messageChannel-meta.xml

For more information, refer Create a Message Channel

<?xml version="1.0" encoding="UTF-8" ?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>SelectedEntityId</masterLabel>
    <isExposed>true</isExposed>
    <description>Message Channel to pass a entity Id</description>
    <lightningMessageFields>
        <fieldName>entityInfo</fieldName>
        <description>This is the record information that selected</description>
    </lightningMessageFields>
</LightningMessageChannel>


Lets create publisher component, here it is showAccountList which will display accounts and clicking on the Account it will publish events.

showAccountList component:

HTML will like below, here onclick event has been used with div.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<template> 
    <lightning-card title="Lightning Message Service: communication between LWC Components" 
                icon-name="custom:custom30">
    <div class="c-container">  
        <lightning-layout>
            <lightning-layout-item padding="around-small">   
                <template if:true={accounts.data}>                
                    <template for:each={accounts.data} for:item="account">
                        <div class="column" key={account.Id}
                            data-item={account.Id} onclick={handleClick}>
                            <div class="slds-text-heading_small">{account.Name}</div>
                        </div>
                    </template>
                </template>
            </lightning-layout-item>
        </lightning-layout>
    </div>
    </lightning-card>
</template> 

showAccountList.js

Few notable points in this js file:
  • Importing message channel to communicate via LMS.

// Import message service features required for publishing and the message channel
import { publish, MessageContext } from 'lightning/messageService';
import selectedEntity from '@salesforce/messageChannel/Selected_EntityId__c';
  • Retrieving Account information from Controller importing getAccountLocations
  • Publishing a message where entire account information is collected as an array and sending as payload in handleClick() event.
Entire js file as follows:

 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
/* eslint-disable no-console */
import { LightningElement, wire} from 'lwc';
import getAccountLocations from '@salesforce/apex/AccountHelper.getAccountLocations';

// Import message service features required for publishing and the message channel
import { publish, MessageContext } from 'lightning/messageService';
import selectedEntity from '@salesforce/messageChannel/Selected_EntityId__c';

export default class ShowAccountList extends LightningElement {
    
    @wire(MessageContext)
    messageContext;
    
    //retrieve account records from database via Apex class
    @wire(getAccountLocations) accounts;

    //clicking the div fires this event
    handleClick(event) {
        //retrieve AccountId from div
        let accountId = event.target.dataset.item;
        let arr = this.accounts.data.find(element => element.Id == accountId);
        let payload = { record: arr };
        publish(this.messageContext, selectedEntity, payload);       
    }       
}

AccountHelper.cls

getAccountLocations method it fetches Account data based on SOQL query. I want to show specific records for demo, that's why filter condition has been added.

public with sharing class AccountHelper {
    @AuraEnabled (cacheable=true)
    public static List<Account> getAccountLocations(){
        return[SELECT Id, Name, BillingStreet, BillingCity, 
    BillingState, BillingPostalCode, BillingCountry 
                FROM Account
                WHERE isDisplay__c = true];
    }
}

Screen will look like this:

showAccountList

Let's move to subscriber component which subscribes for the event and display account location in the map.

displayLocationSubscriber component

It will be like this:

displayAccountSubscriber

Code as follows where lightning-map is used to display location.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<template>
    <lightning-card title="Lightning Map component will show selected Company Location" 
        icon-name="custom:custom30">
        <template if:true={isDisplayLocation}>
            <div class="slds-text-heading_small">{selectedAccountName}</div>                         
            <lightning-map
                map-markers={mapMarkers}
                markers-title={selectedAccountName}
                zoom-level = {zoomLevel}
                center = {center}>
            </lightning-map>
        </template>        
    </lightning-card>
</template>

displayLocationSubscriber.js

Few important points:
  • Defining the scope where subscriber component receives the message in our application. Here APPLICATION_SCOPE has been used.
  • Scope feature is only available using @wire(MessageContext)
  • There are methods to subscribe, unsubscribe and import message channel for using it.
  • In the subscribe method, you can see arrow function has been used so it can call any methods of the class using this operator. Scope can be passed as parameter.
  • handleMessage() method retrieves the account array and prepare attribute values to be used in lightning-map.
Entire js file as follows:


 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { LightningElement, wire, track} from 'lwc';
// Import message service features required for subscribing and the message channel
import {
    subscribe,
    unsubscribe,
    APPLICATION_SCOPE,
    MessageContext
} from 'lightning/messageService';
import selectedEntity from '@salesforce/messageChannel/Selected_EntityId__c';

let acct;
export default class DisplayLocationSubscriber extends LightningElement {
    subscription = null;
    record;

    isDisplayLocation=false;
    @track mapMarkers = []; //holds account location related attributes
    markersTitle = ''; //title of markers used in lightning map.
    zoomLevel = 4;   //initialize zoom level
    center; //location will be displayed in the center of the map

    selectedAccountName = '';

    @wire(MessageContext)
    messageContext;

    // Standard lifecycle hooks used to subscribe and unsubsubscribe to the message channel
    connectedCallback() {
        this.subscribeToMessageChannel();
        this.isDisplayLocation = false;
    }

    disconnectedCallback() {
        this.unsubscribeToMessageChannel();
    }

    // Encapsulate logic for Lightning message service subscribe and unsubsubscribe
    subscribeToMessageChannel() {
        if (!this.subscription) {
            this.subscription = subscribe(
                this.messageContext,
                selectedEntity,
                (message) => this.handleMessage(message),
                { scope: APPLICATION_SCOPE }
            );
        }
    }

    unsubscribeToMessageChannel() {
        unsubscribe(this.subscription);
        this.subscription = null;
    }

    // Handler for message received by component
    handleMessage(message) {
        //assigns account records
        acct = message.record;

        this.selectedAccountName = acct.Name;
        
        //prepares information for the lightning map attribute values.
        this.markersTitle = acct.Name;
        
        this.mapMarkers = [
            {
                location: {
                    Street: acct.BillingStreet,
                    City: acct.BillingCity,
                    State: acct.BillingState,
                    Country: acct.BillingCountry,
                },
                icon: 'custom:custom26',
                title: acct.Name,
            }                                    
        ];
        this.center = {
            location: {
                City: acct.BillingCity,
            },
        };
        this.zoomLevel = 6;
        this.isDisplayLocation = true;
    }    
}

Create a Lightning App Builder page with two regions and place those components and display the page, selecting an account on left side component will display location on right side.

Finally we are done and thanks for reading.


Further Reading


Monday, November 9, 2020

Build Configurable Dynamic Table from Field Set using Lightning Web Component

 

Motivation behind this


I have been looking for building configurable Related list component using Field Set and Lightning Web Component. Earlier I have written blog on  Describe Objects and Retrieve Records using Salesforce Lightning Web Components flavored with Dynamic Datatable.

Now, I want to move it one step further using field set. This approach will help many developers to quickly develop and leverage this concepts.

Here is a justification why it is needed to build.

Use Case


Business has requirement to view the Record detail page as well as its Related list. User has edit and delete permissions to the related list's objects. But business wants that user will be not see any options to edit or delete records from the related list.

For example, Case related list is available under Account or Contact record. User has a permission to edit or delete on Case object but those records only be available for view purpose. User will not able to see Edit/Delete option.

Only option is to built this component as read-only viewing purpose.

This requirement is applicable for similar use cases like this, so it will be better to create a configurable component using Field Set.

Fields from the Field Set will be displayed as columns and records based on those fields will be displayed as rows.

Possible End Result


After building the use case, it will look like this:



Solution Approach


Refer this video for three step solutions:


For an example purpose, I have tried to build Case object as Related List.


First Step

Create a Field Set on Case Object and add necessary fields on the layout. For example name is CaseRelatedListFS


Second Step

Build a LWC Component which will take following configurable attributes:
  • Related Object API Name - Here it is Case
  • Field Set Name - The name of field set defined on the Object whose fields will be displayed as table columns. Here it is CaseRelatedListFS
  • Reference API Name - It is the field based on which query to be fired. Here, it is ContactId, as Case related list will be placed on Contact Record Page.
  • First Column As RecordHyperlink - It will take Yes/No. For example, if we define CaseNumber as first column and if we choose Yes option then Case Number will be shown as hyperlink and clicking on that link, it will be navigated to Case Detail page. If we choose No then, it will be same for other columns.
Let's see this lwcFieldSetComponent.js-meta.xml where those parameters can be configured as targetConfig property.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage,lightning__AppPage,lightning__HomePage">
            <property name="SFDCobjectApiName" label="Related Object API Name" type="String" default=""/>
            <property name="fieldSetName" label="Field Set Name" type="String" default=""/>
            <property name="criteriaFieldAPIName" label="Reference FieldAPIName" type="String" default=""
                            description="The field on which query to be performed. 
                            e.g. it can be AccountId from which Case records will be fetched."/>
            <property name="firstColumnAsRecordHyperLink" label="First Column As RecordHyperLink" 
                            type="String" datasource="Yes,No" default="Yes"/>                        
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Approach has been taken following way:
  • Create a LWC component adding a lightning-datatable which will be populated on load.
  • In the connectedCallback method of js it will fetch fields and records from the database
  • Finally display those in the datatable.
lwcFieldSetComponent.html


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<template>    
    <div class="c-container">
        <div class="slds-card">
            <div class="slds-media__body">
                <h2 class="slds-card__header-title">
                    <span>{lblobjectName} Records ({recordCount})</span>
                </h2>
            </div>
            <div class="slds-card__body">
                <lightning-datatable 
                    key-field="Id"
                    data={tableData}
                    columns={columns}
                    min-column-width=200>
                </lightning-datatable> 
            </div>                           
        </div> 
    </div>    
</template>

lwcFieldSetComponent.js

Few important points to refer here:


  • Never define an attribute as objectApiName as it is reserved, that's why I have used SFDCobjectApiName, otherwise, it will always take current object.

  • When we pull entries from a Map which has been stored in Apex class, the index is inversed. For example, I have first stored FIELD_LIST and then RECORD_LIST in the Map. Now, the index of keys will be 1 for FIELD_LIST and 0 from RECORD_LIST.

  • Extra coding to handle hyperlink record navigation in the first column.


Rest of the comments in the code is self-explanatory.


  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
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
/*
 *   Author: Santanu Boral
*/
import { LightningElement, api, track } from 'lwc';
import getFieldsAndRecords from '@salesforce/apex/FieldSetHelper.getFieldsAndRecords';

export default class LwcFieldSetComponent extends LightningElement {
    
    @api recordId;  // record id from record detail page e.g. ''0012v00002WCUdxAAH'
    @api SFDCobjectApiName; //kind of related list object API Name e.g. 'Case'
    @api fieldSetName; // FieldSet which is defined on that above object e.g. 'CaseRelatedListFS'
    @api criteriaFieldAPIName; // This field will be used in WHERE condition e.g.'AccountId'
    @api firstColumnAsRecordHyperLink; //if the first column can be displayed as hyperlink

    @track columns;   //columns for List of fields datatable
    @track tableData;   //data for list of fields datatable
    
    recordCount; //this displays record count inside the ()
    lblobjectName; //this displays the Object Name whose records are getting displayed

    connectedCallback(){
        let firstTimeEntry = false;
        let firstFieldAPI;

        //make an implicit call to fetch records from database
        getFieldsAndRecords({ strObjectApiName: this.SFDCobjectApiName,
                                strfieldSetName: this.fieldSetName,
                                criteriaField: this.criteriaFieldAPIName,
                                criteriaFieldValue: this.recordId})
        .then(data=>{        
            //get the entire map
            let objStr = JSON.parse(data);   
            
            /* retrieve listOfFields from the map,
             here order is reverse of the way it has been inserted in the map */
            let listOfFields= JSON.parse(Object.values(objStr)[1]);
            
            //retrieve listOfRecords from the map
            let listOfRecords = JSON.parse(Object.values(objStr)[0]);

            let items = []; //local array to prepare columns

            /*if user wants to display first column has hyperlink and clicking on the link it will
                naviagte to record detail page. Below code prepare the first column with type = url
            */
            listOfFields.map(element=>{
                //it will enter this if-block just once
                if(this.firstColumnAsRecordHyperLink !=null && this.firstColumnAsRecordHyperLink=='Yes'
                                                        && firstTimeEntry==false){
                    firstFieldAPI  = element.fieldPath; 
                    //perpare first column as hyperlink                                     
                    items = [...items ,
                                    {
                                        label: element.label, 
                                        fieldName: 'URLField',
                                        fixedWidth: 150,
                                        type: 'url', 
                                        typeAttributes: { 
                                            label: {
                                                fieldName: element.fieldPath
                                            },
                                            target: '_blank'
                                        },
                                        sortable: true 
                                    }
                    ];
                    firstTimeEntry = true;
                } else {
                    items = [...items ,{label: element.label, 
                        fieldName: element.fieldPath}];
                }   
            });
            //finally assigns item array to columns
            this.columns = items; 
            this.tableData = listOfRecords;

            console.log('listOfRecords',listOfRecords);
            /*if user wants to display first column has hyperlink and clicking on the link it will
                naviagte to record detail page. Below code prepare the field value of first column
            */
            if(this.firstColumnAsRecordHyperLink !=null && this.firstColumnAsRecordHyperLink=='Yes'){
                let URLField;
                //retrieve Id, create URL with Id and push it into the array
                this.tableData = listOfRecords.map(item=>{
                    URLField = '/lightning/r/' + this.SFDCobjectApiName + '/' + item.Id + '/view';
                    return {...item,URLField};                     
                });
                
                //now create final array excluding firstFieldAPI
                this.tableData = this.tableData.filter(item => item.fieldPath  != firstFieldAPI);
            }

            //assign values to display Object Name and Record Count on the screen
            this.lblobjectName = this.SFDCobjectApiName;
            this.recordCount = this.tableData.length;
            this.error = undefined;   
        })
        .catch(error =>{
            this.error = error;
            console.log('error',error);
            this.tableData = undefined;
            this.lblobjectName = this.SFDCobjectApiName;
        })        
    }
}

FieldSetHelper.cls

Few notable points on this getFieldsAndRecords method:

  • Getting the instance of SObject based on strObjectApiName, the reflection has been used which is way more faster that globalDescribe.
  • Use of Schema.FieldSetMember to get the fields from Field Set. Refer Documentation
  • getFieldPath() of FieldSetMember gives the fieldAPI which has been used to build SOQL query
Finally, both field List and record List are getting stored in the Map and returned to js.


 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
public with sharing class FieldSetHelper {
    @AuraEnabled (cacheable=true)
    public static String getFieldsAndRecords(String strObjectApiName, String strfieldSetName,
                                             String criteriaField, String criteriaFieldValue){
        Map<String, String> returnMap = new Map<String,String>();
        if(!String.isEmpty(strObjectApiName) && !String.isEmpty(strfieldSetName)){
            //get fields from FieldSet
            SObject sObj = (SObject)(Type.forName('Schema.'+ strObjectApiName).newInstance());
            List<Schema.FieldSetMember> lstFSMember = 
                sObj.getSObjectType().getDescribe().fieldSets.getMap().get(strfieldSetName).getFields();

	    //prepare SOQL query based on fieldAPIs	
	    String query = 'SELECT ';
	    for(Schema.FieldSetMember f : lstFSMember) {
	        query += f.getFieldPath() + ', ';
            }
            query += 'Id FROM ' + strObjectApiName ;

            //Just in case criteria field not specified then it will return all records
            if(!(String.isEmpty(criteriaField) && String.isEmpty(criteriaFieldValue))){
                query += ' WHERE ' + criteriaField + '=\'' + criteriaFieldValue + '\'';
            }
                        
	    //execute query
             List<SObject> lstRecords = Database.query(query);
            
             //prepare a map which will hold fieldList and recordList and return it
	     returnMap.put('FIELD_LIST', JSON.serialize(lstFSMember));
	     returnMap.put('RECORD_LIST', JSON.serialize(lstRecords));
	     return JSON.serialize(returnMap);
        }
        return null;
    }
}

Final Step

After building the component, place it on Record Page through App Builder and define attribute values.



You can see that component is displaying fields and records.

This concept can be leveraged easily at any project. The component can be improvised more like, handling events, based on Salesforce field type Datatable column types can be defined, it could be good to incorporate paginations etc, but it is a good start.

We are done and thanks for reading.



Further Reading