LWC:
Lightning web component is a new programming model to developer salesforce lightning components. It's a UI framework that is built using native HTML and javascript.
Decorator is a special kind of declaration that can change behaviour of method or property.
LWC support 3 decorators. (@track, @api, @wire)
@track: To make property private reactive.
@api: To make property public reactive.
@wire: To read data from Salesforce and store into cache.
A lifecycle hook is a callback method triggered at a specific phase of a component instance’s lifecycle.
Lightning web components have a lifecycle managed by the framework. The framework creates components, inserts them into the DOM, renders them, and removes them from the DOM. It also monitors components for property changes. Generally, components don’t need to call these lifecycle hooks, but it is possible.
- constructor()
- connectedCallback()
- disconnectedCallback()
- render()
- renderedCallback()
- errorCallback(error, stack)
- Run Code When a Component Is Created
Theconstructor()
method fires when a component instance is created. Don’t add attributes to the host element during construction. You can add attributes to the host element in any other lifecycle hook.
- Run Code When a Component Is Inserted or Removed from the DOM
TheconnectedCallback()
lifecycle hook fires when a component is inserted into the DOM. ThedisconnectedCallback()
lifecycle hook fires when a component is removed from the DOM. - Run Code When a Component Renders
TherenderedCallback()
is unique to Lightning Web Components. Use it to perform logic after a component has finished the rendering phase. - Handle Component Errors
TheerrorCallback()
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. 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.
sampleFirstTemplate.html
<!-- firstTemplate.html -->
<template>
<lightning-card>
<div>
Primary Template Rendered
</div>
</lightning-card>
<c-child-component-test></c-child-component-test>
</template>
sampleSecondTemplate.html
<!-- secondTemplate.html -->
<template>
<lightning-card title="Template Two">
<div>
Secondary Template rendered
</div>
</lightning-card>
</template>
sampleHelloWorld.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__AppPage</target> <target>lightning__RecordPage</target> <target>lightning__HomePage</target> </targets> </LightningComponentBundle>
public class AccountController
{
@AuraEnabled(cacheable=true)
public static List<Account> displayAccounts(){
return [select Id,Name,Site from Account];
}
@AuraEnabled
public static List<Account> updateRecord(String accId){
Account acc=[select Id,Name,Site from Account where Id=:accId];
acc.site='Approved';
try{
update acc;
}
catch (Exception e) {
System.debug('unable to update the record due to'+e.getMessage());
}
return displayAccounts();
}
}
DisplayAndUpdateAccount.html
<template>
<table class="slds-table slds-table_cell-buffer slds-table_bordered">
<tr>
<td><b>Name</b></td>
<td><b>Site</b></td>
<td><b>Action</b></td>
</tr>
<template for:each={accounts.data} for:item="acc">
<tr key={acc.Id}>
<td>
{acc.Name}
</td>
<td>
<lightning-button label="update" variant="brand" value={acc.Id} onclick={handleUpdate}>
</lightning-button>
</td>
</tr>
</template>
</table>
</template>
</template>
DisplayAndUpdateAccount.js
import { LightningElement,api,wire } from 'lwc';
import displayAccounts from '@salesforce/apex/AccountController.displayAccounts';
import updateRecord from '@salesforce/apex/AccountController.updateRecord';
import { refreshApex } from '@salesforce/apex';
export default class UpdateAccount extends LightningElement {
@api currentRecordId;
@api errorMessage;
@wire(displayAccounts) accounts;
handleUpdate(event){
this.currentRecordId=event.target.value;
console.log('@@currentRecordId@@@'+this.currentRecordId);
updateRecord({
accId:this.currentRecordId
})
.then(() => {
console.log('SUCCESS');
return refreshApex(this.accounts);
})
.catch((error) => {
this.errorMessage=error;
console.log('unable to update the record due to'+JSON.stringify(this.errorMessage));
});
}
}
4. Call Apex Methods Imperatively:
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContactList() {
return [
SELECT Id, Name, Title, Phone, Email, Picture__c
FROM Contact
WHERE Picture__c != NULL
WITH SECURITY_ENFORCED
LIMIT 10
];
}
}
// apexImperativeMethod.js
import { LightningElement, track } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';
export default class ApexImperativeMethod extends LightningElement {
@track contacts;
@track error;
handleLoad() {
getContactList()
.then(result => {
this.contacts = result;
})
.catch(error => {
this.error = error;
});
}
}
<!-- apexImperativeMethod.html -->
<template>
<lightning-card title="ApexImperativeMethod" icon-name="custom:custom63">
<div class="slds-m-around_medium">
<p class="slds-m-bottom_small">
<lightning-button label="Load Contacts" onclick={handleLoad}></lightning-button>
</p>
<template lwc:if={contacts}>
<template for:each={contacts} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template lwc:elseif={error}>
<c-error-panel errors={error}></c-error-panel>
</template>
</div>
</lightning-card>
</template>
Lightning Data Service (LDS) is an intermediate layer between Lightning Components (both LWC and Aura) and User Interface API of Salesforce,
In lightning web components you can use LDS in two different ways
- Standard Lightning web components
lightning-record-edit-form
,lightning-record-form
, andlightning-record-view-form
. -- Lightning record forms. - Lightning UI APIs, like
uiRecordApi
,uiObjectApi
which contains wired adapters functions.
import { createRecord } from 'lightning/uiRecordApi'; used to create record.
getObjectInfo
Use this wire adapter to get metadata about a specific object. The response includes metadata describing the object’s fields, child relationships, record type, and theme.
- Syntax
import { LightningElement, wire } from 'lwc';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
export default class Example extends LightningElement {
@wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
propertyOrFunction;
}
lightningRecordFormEditExampleLWC.html
1 2 3 4 5 | < template > < lightning-record-form record-id={recordId} object-api-name={objectApiName} fields={fields} columns = "2" mode = "edit" onsubmit={handleSubmit}> </ lightning-record-form > </ template > |
lightningRecordFormEditExampleLWC.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import { LightningElement, api } from 'lwc' ; import NAME_FIELD from '@salesforce/schema/Account.Name' ; import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue' ; import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry' ; export default class LightningRecordFormEditExampleLWC extends LightningElement { @api recordId; @api objectApiName; fields = [NAME_FIELD, REVENUE_FIELD, INDUSTRY_FIELD]; handleSubmit(event){ //you can change values from here //const fields = event.detail.fields; //fields.Name = 'My Custom Name'; // modify a field console.log( 'Account detail : ' ,event.detail.fields); console.log( 'Account name : ' ,event.detail.fields.Name); } } |
lightningRecordFormEditExampleLWC.js-meta.xml
1 2 3 4 5 6 7 8 9 10 | <? xml version = "1.0" encoding = "UTF-8" ?> < apiVersion >48.0</ apiVersion > < isExposed >true</ isExposed > < targets > < target >lightning__AppPage</ target > < target >lightning__RecordPage</ target > < target >lightning__HomePage</ target > </ targets > </ LightningComponentBundle > |
Now we can add this LWC component on the Account Record page.
We can change html file to show full page layout
lightningRecordFormEditExampleLWC.html
1 2 3 4 5 | < template > < lightning-record-form record-id={recordId} object-api-name = "Account" layout-type = "Full" columns = "2" mode = "edit" onsuccess={handleSuccess} onsubmit={handleSubmit}> </ lightning-record-form > </ template >
|
Creating a Record using lightning-record-form LWC
lightningRecordFormCreateExampleLWC.html
1 2 3 4 5 | < template > < lightning-record-form object-api-name={objectApiName} fields={fields} onsuccess={handleSuccess}> </ lightning-record-form > </ template > |
lightningRecordFormCreateExampleLWC.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import { LightningElement, api } from 'lwc' ; import { ShowToastEvent } from 'lightning/platformShowToastEvent' ; import NAME_FIELD from '@salesforce/schema/Account.Name' ; import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue' ; import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry' ; export default class LightningRecordFormCreateExampleLWC extends LightningElement { // objectApiName is "Account" when this component is placed on an account record page @api objectApiName; fields = [NAME_FIELD, REVENUE_FIELD, INDUSTRY_FIELD]; handleSuccess(event) { const evt = new ShowToastEvent({ title: "Account created" , message: "Record ID: " + event.detail.id, variant: "success" }); this .dispatchEvent(evt); } } |
lightningRecordFormCreateExampleLWC.js-meta.xml
1 2 3 4 5 6 7 8 9 10 | <? xml version = "1.0" encoding = "UTF-8" ?> < apiVersion >48.0</ apiVersion > < isExposed >true</ isExposed > < targets > < target >lightning__AppPage</ target > < target >lightning__RecordPage</ target > < target >lightning__HomePage</ target > </ targets > </ LightningComponentBundle > |
Now we can add this LWC component on the Account Record page.
Let’s create an example to create account record using lightning-record-edit-form in Lightning Web Component
recordEditFormCreateExampleLWC.html
1 2 3 4 5 6 7 8 9 10 11 12 | < template > < lightning-record-edit-form object-api-name = "Contact" onsuccess={handleSuccess} onsubmit ={handleSubmit}> < lightning-messages > </ lightning-messages > < lightning-input-field field-name = 'AccountId' ></ lightning-input-field > < lightning-input-field field-name = 'FirstName' ></ lightning-input-field > < lightning-input-field field-name = 'LastName' ></ lightning-input-field > < lightning-input-field field-name = 'Email' ></ lightning-input-field > < lightning-button class = "slds-m-top_small" type = "submit" label = "Create Contact" > </ lightning-button > </ lightning-record-edit-form > </ template > |
recordEditFormCreateExampleLWC.js
1 2 3 4 5 6 7 8 9 | import { LightningElement} from 'lwc' ; export default class RecordEditFormCreateExampleLWC extends LightningElement { handleSuccess(event) { console.log( 'onsuccess event recordEditForm' ,event.detail.id) } handleSubmit(event) { console.log( 'onsubmit event recordEditForm' + event.detail.fields); } } |
recordEditFormCreateExampleLWC.js-meta.xml
1 2 3 4 5 6 7 8 9 10 | <? xml version = "1.0" encoding = "UTF-8" ?> < apiVersion >48.0</ apiVersion > < isExposed >true</ isExposed > < targets > < target >lightning__AppPage</ target > < target >lightning__RecordPage</ target > < target >lightning__HomePage</ target > </ targets > </ LightningComponentBundle > |
Now we can add this LWC component on the Contact Record page.
- Go to Contact record.
- Click Setup (Gear Icon) and select Edit Page.
- Under Custom Components, find your recordEditFormCreateExampleLWC component and drag it on Contact Page.
- Click Save and activate.
Add the following code in childLwc component:
export default class ChildLwc extends LightningElement {
message;
@api greet(message) {
// Suppose a translating capability is here which translate the message param to local user's language
this.message = message;
}
}
At Line 8 in the above code, we have decorated the greet method with @api so that it is publically available to be called from the parent component.
Add the following code in parentLwc component:
<template>
<lightning-card title="Parent LWC">
<lightning-button variant="brand" label="Send Message" title="Send Message" slot = "actions" onclick={parentHandler}></lightning-button>
<c-child-lwc onsendmessage={parentHandler}></c-child-lwc>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class ParentLwc extends LightningElement {
parentHandler(event) {
this.template.querySelector('c-child-lwc').greet('Hey, This Message Is Sent From Parent!');
} }
In the above code, we are calling the child method by using this.template.querySelector(‘c-child-lwc’)
querySelector() returns the first Element within the document that matches the specified selector.
View the parentLwc component on the lightning app page, you would be getting the below output.
Step 1 : Here we will create a Lightning Message Service (LMS) file in the messageChannels folder in the default branch.
TechdicerChannel.messageChannel-meta.xml :
1 2 3 4 5 6 7 8 9 10 | <?xml version="1.0" encoding="UTF-8"?> <LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata"> <description>This is a Lightning Message Service Channel.</description> <isExposed>true</isExposed> <lightningMessageFields> <description>This is sample message</description> <fieldName>message</fieldName> </lightningMessageFields> <masterLabel>TechdicerChannel</masterLabel> </LightningMessageChannel> |
Step 2: In this step, we will create two LWC component which is not related to each other, one is the Publisher and another one is the Subscriber component.
lWCLMS.HTML : this is the Publisher component.
1 2 3 4 5 6 7 8 9 10 11 | < template > < lightning-card title = "Lightning Message Service (LMS) in LWC Publisher" icon-name = "standard:contact" > < div class = "slds-p-horizontal_small" > < lightning-input type = "text" label = "Message" onchange={handleChange} value={message}></ lightning-input > <!-- call subscriber --> < div class = "slds-p-around_medium lgc-bg" style = "text-align: end;" > < lightning-button label = "Fire Event" variant = "brand" onclick={handleClick}></ lightning-button > </ div > </ div > </ lightning-card > </ template > |
lWCLMS.JS :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import { LightningElement, wire } from 'lwc' ; import TechdicerChannel from '@salesforce/messageChannel/TechdicerChannel__c' ; import {publish, MessageContext} from 'lightning/messageService' export default class LWCLMS extends LightningElement { @wire(MessageContext) messageContext; message; handleChange(event){ this .message = event.detail.value; } handleClick() { let message = {message: this .message}; publish( this .messageContext, TechdicerChannel, message); } } |
subscriber.HTML : This is the Subscriber Component.
1 2 3 4 5 6 7 | < template > < lightning-card title = "Lightning Message Service (LMS) in LWC Subscriber" icon-name = "standard:contact" > < div class = "slds-m-around_medium" > {publisherMessage} </ div > </ lightning-card > </ template > |
subscriber.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 35 36 37 38 | import { LightningElement, wire } from 'lwc' ; import TechdicerChannel from '@salesforce/messageChannel/TechdicerChannel__c' ; import { subscribe, MessageContext } from 'lightning/messageService' ; import { ShowToastEvent } from 'lightning/platformShowToastEvent' ; export default class Subscriber extends LightningElement { publisherMessage = '' ; subscription = null ; @wire(MessageContext) messageContext; connectedCallback() { this .handleSubscribe(); } handleSubscribe() { if ( this .subscription) { return ; } this .subscription = subscribe( this .messageContext, TechdicerChannel, (message) => { console.log(message.message); this .publisherMessage = message.message; this .ShowToast( 'Success' , message.message, 'success' , 'dismissable' ); }); } ShowToast(title, message, variant, mode){ const evt = new ShowToastEvent({ title: title, message:message, variant: variant, mode: mode }); this .dispatchEvent(evt); } } |
Output :
Async and Await in Lightning Web Components
Async and Await in Lightning Web Components
Asynchronous programming is a crucial aspect of web development, and it becomes even more important when dealing with large-scale applications.
In traditional JavaScript, asynchronous programming is typically handled with callbacks or promises. However, the introduction of the async/await syntax has greatly simplified asynchronous programming in JavaScript. This syntax is available in Lightning Web Components as well and can help to simplify your code and make it more readable.
What is Async and Await?
Async/await is essentially a way to write asynchronous code that looks and behaves more like synchronous code. Async/await is built on top of promises, which are used to handle asynchronous operations in JavaScript.
The async keyword is used to define an asynchronous function, which returns a promise. Within this function, you can use the await keyword to pause the execution of the function until a promise is resolved or rejected. This makes your code look more like synchronous code, as the execution is blocked until the promise resolves.
How to use Async and Await in Lightning Web Components
Now that we understand what async/await is, let's take a look at how we can use it in Lightning Web Components. The first step is to define an asynchronous function. Here's an example of an asynchronous function that returns a promise:
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
In this example, we're using the fetch API to make a request to an API endpoint. We're using the await keyword to pause the execution of the function until the promise returned by fetch is resolved.
Once the promise is resolved, we parse the response body as JSON using the response.json() method. Finally, we return the resulting data.
To use this function in a Lightning Web Component, we can simply call it from the component's JavaScript file:
import { LightningElement } from 'lwc';
import fetchData from './fetchData';
export default class MyComponent extends LightningElement {
async connectedCallback() {
const data = await fetchData();
console.log(data);
}
}
Promise with Async and Await
import { LightningElement, api } from 'lwc';
import fetchData from '@salesforce/apex/MyController.fetchData';
export default class MyComponent extends LightningElement {
@api recordIds;
data = [];
async connectedCallback() {
const promises = this.recordIds.map(id => fetchData({ recordId: id }));
const results = await Promise.all(promises);
this.data = results;
}
}
Common Use Cases for Async and Await
As you can see, the use of async and await is not limited to just making API calls. In fact, they're very useful when you want to perform an action that takes a long time. For example:
Making HTTP requests
Waiting for an asynchronous action to finish
Processing multiple promises in parallel
Error Handling with Async and Await
As you've seen, async functions return promises. If an exception is thrown in an async function, the promise it returns will be rejected with that exception. This can be handled by catching errors with try/catch blocks as usual:
try {
await someAsyncFunction(arg1); // do something else here...
} catch(e) { // handle error here... }