Tuesday, April 30, 2024

Communicate Between Unrelated LWC Components- Lightning Message Service (LMS)

LMS is a publish and subscribe service that helps communicating between LWC, Aura components, and Visualforce pages. 

In this, first we have to defined channels called as messaging channel to define attributes through which we can publish information. Both the components (publisher and subscriber) needs to import this channel. Consider message channel as protocol through which information will be shared between 2 components.

In order to define messaging channel, you have create a folder named as "messageChannels" under default folder in VS code.



Now create a new file named as "enquiry_sender.messageChannel-meta.xml". You can give any name to file but make sure that it has extension as ".messageChannel-meta.xml".

In this xml file you define, lightningMessageFields and its description. These fields names will be used to used information from one component to another.


Note: 

If you are getting a validation error for the XML, then turn XML validation off.

In VS Code, click File > Preferences > Settings (Windows) or Code > Settings > Settings (macOS). Search for Validation. Select XML and uncheck Validation.

Now we have defined a message channel, lets use it to publish and subscriber message.

Publish LMS

In order to publish message using this channel, we need to import LMS and channel that we created

import { publish, MessageContext } from 'lightning/messageService';
import SEND_ENQUIRY_CHANNEL from '@salesforce/messageChannel/Enquiry_Sender__c';

Use below syntax to publish the LMS:

    @wire(MessageContext)
    messageContext;
    const payload = {
            email: 'sk@gmail.com',
            phone: '0123456789'
          };
          console.log('***payload:'+JSON.stringify(payload));
          publish(this.messageContext, SEND_ENQUIRY_CHANNEL, payload);


Subscribe LMS

In order to subscribe LMS, subscribing also need to import LMS and channel that we have been created for communication.

import { subscribe, MessageContext } from 'lightning/messageService';
import SEND_ENQUIRY_CHANNEL from '@salesforce/messageChannel/Enquiry_Sender__c';

Use below syntax to subscribe LMS:

    @wire(MessageContext)
    messageContext;
    subscribeToLMS() {
        this.subscription = subscribe(
        this.messageContext,
        SEND_ENQUIRY_CHANNEL,
        (message) => this.handleMessage(message)
        );
    }
    handleMessage(message) {
        console.log('**recieved message:'+JSON.stringify(message));
    }

    connectedCallback() {
        this.subscribeToLMS();
    }

In this way information can be passed between 2 LWC components which are not related to each other.

I have created 2 LWC components for demo purpose to show how it works. In first LWC component (userEnquiry), we take email and phone from user and when he clicks on send button, a message is published. Now we have another LWC component (contactCheck) which will subscribe to send_enquiry__c channel. 

So whenever first component will publish message, another LWC component will display information send by first component.




Below is complete code for reference:


Note:
LMS is powerful, effective, and easy to use, but use it only if it is necessary. Firing DOM events is much more efficient and should be first preference.

Hope this will help!!

Friday, March 1, 2024

Lifecycle Hooks in LWC (Lightning Web Components)

 A lifecycle hook is a callback method which is called automatically at a specific phase of a component lifecycle. These are managed by LWC framework. These hooks get called at different phases like initialization of components, when elements gets inserted in DOM or removed from DOM, rendering of components based on property changes etc.

Lets go through different lifecycle hooks:

constructor()

  • Whenever component is created, this function gets called.
  • This hook flows from parent to child, which means that it fires in the parent first.
  • As child element do not exist, you cannot access them.
  • Properties are mainly assigned to component after constructor() and before connectedCallback()
  • If you define constructor() callback then you should always super() from constructor(). This is because every LWC component extends LightningElement and since LightningElement has its own constructor, so we should first invoke the parent constructor.
import { LightningElement } from 'lwc'
export default class ConstructorCallbackExample extends LightningElement{
	constructor(){
		super();
	}
}


connectedCallback()

  • When the element is inserted into a document, this function gets called.
  • This hook flows also from parent to child. So you can’t access child elements because they don’t exist yet.
  • Properties are assigned to component so this callback can be used to perform or fetch initial data set.
  • If you manipulate the elements means adding or removing element from component, then connectedCallback() will fire more than once. So always put some checks if you don’t want to run particular logic everytime.
  • connectedCallback() can be used to Subscribe and Unsubscribe from a Message Channel.
import { LightningElement } from 'lwc'
export default class ConnectedCallbackExample extends LightningElement{
	connectedCallback(){
		console.log('***connected callback is called');
	}
}


renderedCallback()

  • renderedCallback() gets called after every render of component. Whenever value of reactive component changes, component get rendered.
  • renderedCallback() is specific to LWC and it has no dependency in HTML custom elements specifications.
  • renderedCallback() flows from child to parent.
  • Remember, updating state of component can cause infinite loop. So don’t update a public property or field in renderedCallback(). If you perform changes to reactive attributes, guard them or they can trigger wasteful rerenders or an infinite rendering loop.
  • Also don’t update a wire adapter configuration object property in renderedCallback(). 

disconnectedCallback()
  • Whenever an element is removed from DOM, then disconnectedCallback() is called.
  • This hook flows from parent to child.
  • Use disconnectedCallback() to clean up work done in the connectedCallback(), like purging caches or removing event listeners or unsubscribe from a message channel.
import { LightningElement } from 'lwc'
export default class disconnectedCallbackExample extends LightningElement{
	disconnectedCallback(){
    	console.log('**** disconnectedCallback is called');
    }
}

render()
  • If there is need to display different UI to end user based on some conditions, then we can have 2 different HTML files and decide which HTML file to render based on conditions.
  • Render() may be called before or after connectedCallback().
  • Imagine that you have a component that can be rendered in two different ways but you don’t want to mix the HTML in one file. Create multiple HTML files in the component bundle. Import them both and add a condition in the render() method to return the correct template depending on the component’s state.
  • Although it’s possible for a component to render multiple templates, it is recommended to use  lwc:if|elseif|else directives to render nested templates conditionally instead.
import { LightningElement } from "lwc";
import templateOne from "./templateOne.html";
import templateTwo from "./templateTwo.html";
export default class MiscMultipleTemplates extends LightningElement {
  showTemplateOne = true;
  render() {
    return this.showTemplateOne ? templateOne : templateTwo;
  }
  switchTemplate() {
    this.showTemplateOne = !this.showTemplateOne;
  }
}


errorCallback(error, stack)
  • The errorCallback(error, stack) hook is called when the component throws an error.
  • The error argument is a JavaScript native error object, and the stack argument is a string.
  • This method works like a JavaScript catch{} block for catching errors.
  • The errorCallback() is unique to Lightning Web Components. Implement it to create an error boundary component that captures errors in all the descendent components in its tree. It captures errors that occur in the descendant's lifecycle hooks or during an event handler declared in an HTML template. errorCallback() catches errors that occurs in the descendant components but not itself. You can code the error boundary component to log stack information and render an alternative view to tell users what happened and what to do next
import { LightningElement } from "lwc";
export default class ErrorCallbackExample extends LightningElement {
  error;
  stack;
  errorCallback(error, stack) {
    this.error = error;
  }
}

Hope this will help!!

Thursday, February 22, 2024

Communicate from Parent to Child Component in LWC

There are few scenarios where we need to pass property/values from parent component to child component in LWC. There are different ways to achieve this:

  • Using @api decorator (public property)

The @api decorator in the child component makes property as public, which can be utilised by parent component to update it. So whenever you add child component in parent component, then specify child public property as an attribute and pass the value from parent component property. 

For example, Child component contains public property "passedUrl" with @api decorator

-----------childComponent.html--------------------

<template>
    <div style="background-color:#b2b689;border-style: solid;">
        <b>Child component</b>
	<p>URL passed from parent-<b>{passedUrl}</b></p>
    </div>
</template>

--------childComponent.js---------------------

import { LightningElement,api} from 'lwc';
export default class SkChildComponent extends LightningElement {
    @api passedUrl;    
}

Now if you have to pass value to "passedUrl" property from parent component then just specify attribute as property name and pass any value from parent component property.

--------parentComponent.html---------------------

<template>
    <lightning-card  title="Communicate from Parent to Child">
        <div style="background-color:#E6E6FA;border-style: ">
            <b>This is parent Component</b>            
            <c-child-component passed-url={selResourceURL} >
            </c-child-component> 
        </div>
	</lightning-card> 
</template>

--------parentComponent.js---------------------

import { LightningElement,track } from 'lwc';
export default class SkParentContainerCmp extends LightningElement {
	@track selResourceURL='https://www.sfdcstuff.com/';
}


  • Using getter setter property in child component
you can define @api property with separate getter setter defined. Whenever the value of this property changes from parent component, your setter method for that property will always get called. Now you can define your custom logic if you have and then utilise that in child component.

--------childComponent.js---------------------

import { LightningElement,api,track } from 'lwc';
export default class SkChildComponent extends LightningElement {
    @track _userInputNumber;
    @api
    get userInputNumber(){
        return this._userInputNumber;
    }
    set userInputNumber(value){
        //write your logic to set any property
        this._userInputNumber=value;
    }
}

  • By accessing child function from parent component
You can decorate the function defined in child component with @api. Now parent component can access this public function. By using this method, you can invoke function defined in child component.

Suppose you have defined below function in child component:

@api
increaseCounter(){
   //your logic
}

Now from parent component you can access this function by using below syntax:

this.template.querySelector('c-child-component').increaseCounter();

I have created parent and child component to explain how this communication from parent to child component happen. Please find below complete code snippet and components snapshot:


Complete Code

Hope this will Help!!

Tuesday, December 26, 2023

Export all Custom Metadata Records in CSV file using Apex

Using apex, we can export all custom metadata records in CSV files. This is sometime needed if we want to review all custom metadata records after sandbox refresh or to check the dependency of any field or attribute in all custom metadata records.


Below is apex class which can be used for this and once you execute its method from developer console, you will receive email with csv that contain all custom metadata records.

Open developer console and open execute anonymous window. Execute below script:

SK_CustomMetadataUtility.exportAllCMRecordsInCSV();


Below is snapshot of csv file:


Note:

  • This script will specify the custom metadata field api name and its value seperated by "&SK&" delimiter.
  • All fields and its values are seperated by comma.
  • You can modify this method to fetch all custom metadata related to installed packages by specifying a parameter for namespace.


Hope this will help!!

 

Sunday, August 20, 2023

Open Standard Email composer from LWC Component - Global Email Quick Action

As we all know that we can create Email Actions in Salesforce but capability to invoke Email Action from LWC components was not present. After Winter' 23, Salesforce allowed to invoke global email action from custom components (from Aura and LWC both).

In this blog, I am going to explain how to invoke email action from LWC component. You can launch email composer from custom button. 

We have to use the lightning-navigation and lightning-page-reference-utils components to create a QuickAction (Global) Send Email action, which opens an email draft with pre-populated field values.

We will create simple LWC component which will have button. Once we click on this button, email composer will open. Below is sample code for reference:


Note:

  • You can default email properties like subject, body, ccAddress, toAddress and fromEmailAddress.
  • You always need to provide record. Whenever you click on send button after composing email, system will create an emailmessage record with relatedToId as specified recordId.
  • encodeDefaultFieldValues is being provide to specify all default values.

Snapshots for reference:




Hope this will help!!






Thursday, August 3, 2023

Automatically Remove Special Characters from Filename while Uploading Files in Salesforce

 As we all know that we can perform URL hack to download all files related to parent record from salesforce. We have observed that sometime downloaded file name is changed to contentversion recordId by salesforce instead of actual file name. 

It is my observation that if file name contains special characters, then when we perform download All, file name is changed to "068xxxxxxxxxxxxxx.pdf".

I have added 2 files to account record for reference:



Now I can generate download URL if I have contentversionId of these 2 files. In my case the download URL will be:

https://xxxx/sfc/servlet.shepherd/version/download/0680K00000kLSdbQAG/0680K00000kLSfwQAG

where xxxx is you org domain URL.

You can use below script to generate download all files URL if you know the parent record Id.


If I open this URL, I will zip file which will contain both files and when I will extract it, it will appear as shown below:


As my observation, if any special character is present in file name then salesforce changes the file name. If "-" character is present, then salesforce won't change file name.

For this kind of scenarios, I recommend to have script in place which will remove all special characters from file name whenever file is being uploaded. You can write trigger on contentVersion object to remove special characters from file name. Below is code snippet which can help:



Hope this will help!!!!



Wednesday, July 5, 2023

FieldDefinition - How to get list of all fields present in any Object using Tooling API in Salesforce

We can utilize Tooling API to get information of all fields present in a object. You can put different filters like to get all fields or specific fields like auto number fields, ExternalID fields etc.

Below is apex class which contains method to get all fields. Remember that callout uses Named Credential in order to do handshake with SFDC org.

This method contains 3 parameters:

  • selOrgNCName : Name of Named Credential created to connect to SFDC org
  • objAPIName : Specify the object API name in order to get list of all fields
  • namespacePrefix : Specify namespacePrefix if you want to retrieve fields related to any installed packages. If you want to get list of fields created in SFDC org, then specify blank or NULL.
Note:
  • I have added filter not to return auto number fields. If you want to get those fields details then remove it from query string.
  • If you want to query only External Id fields then specify filter as DataType+Like+\'%25(External+ID)%25\'';


Hope this will help!!