LWC in Salesforce
Table of contents
Lifecycle Hook in LWC
A lifecycle hook is a callback method that triggers at a specific phase of a component instance’s lifecycle. With LWC, you can use the following lifecycle hooks.
connectedCallback()
anddisconnectedCallback()
Here’s a brief overview of some essential lifecycle hooks:
constructor(): This is where the component is initialized. You can set default values and perform one-time setup.
connectedCallback(): After the component is added to the DOM, this hook runs. It’s a great place to perform DOM manipulations and data retrieval.
render():
Allows you to override the default rendering behavior by specifying a custom template.
Rarely used unless you want to dynamically select a template.
The main use case is to conditionally render a template. Define business logic to decide which template (HTML file) to use. The method must return a valid HTML template.
renderedCallback(): Called after the component’s DOM is updated and rendered. This hook is triggered after rendering occurs. It’s ideal for operations that require knowledge of the rendered DOM.
disconnectedCallback(): When the component is removed from the DOM, this hook is invoked. Use it for cleanup operations and resource releases.
errorCallback(): If an error occurs during rendering, this hook is called. It’s your opportunity to gracefully handle errors.
The
render()
method is not technically a lifecycle hook. It is a protected method on theLightningElement
class.
Decorators in LWC
@api decorator
The
@api
decorator in Lightning Web Components (LWC) is used to expose public properties and methods to other components.This makes them accessible outside of the component, typically by a parent component.
It is the way to define what a component's interface should be, i.e., which properties and methods should be accessible and usable by other components.
@track decorator
- The
@track
decorator in Lightning Web Components (LWC) is used to make properties reactive, meaning that when the value of the property changes, the component will automatically re-render the UI to reflect the updated value.
- The
NOTE: If you want a property to be accessible by a parent component, you should use @api
, not @track
. If you only want the property to be reactive within the component itself but not exposed to parents, use @track
without @api
.
@wire decorator
In Lightning Web Components (LWC), the
@wire
decorator is used to read data from Salesforce and other external services asynchronously.It binds a property or function to a service call and automatically handles the lifecycle of the call. When data is returned or changes, the component automatically re-renders.
Lightning Data Service
Lightning Data Service is a centralized data caching mechanism which is utilized to perform create, read, update or delete on a record without having a server side apex call in Lightning web components.
Records loaded in Lightning Data Service are cached and shared across components. If a page is composed of components showing the same record, all components show the same version of the record. Components accessing the same record see significant performance improvements, because a record is loaded once, no matter how many components are using it.
In a Lightning web component, perform operations on data and access metadata using these technologies built on Lightning Data Service.
Base Components:
lightning-record-edit-form
,lightning-record-form
, andlightning-record-view-form
The base component of Lightning Data Service are
lightning-record-form:
A form with standard lightning UI to create, view or edit a recordlightning-record-edit-form:
A form to create record with specified fields or update fields in an existing recordlightning-record-view-form:
A form to display specific fields data of a record in read-only mode
Advantages of Lightning Data Service
No server side apex call as the record is cached & retrieved from client-side cache of record data that has been loaded via a wire adapter, thus eliminating multiple calls to the server
Lightning Data Service is built on top of User Interface API, the responses respect CRUD access, field-level security, and sharing settings pertaining to a User.
Lightning Record Edit Form
Edit Record
If the
record-id
attribute is provided, the form is used to edit the specified recor
<lightning-record-edit-form
object-api-name="Account"
record-id="001XXXXXXXXXXXXXXX" <!-- Replace with the actual record ID -->
onsuccess={handleSuccess}
>
<lightning-input-field field-name="Name"></lightning-input-field>
<lightning-input-field field-name="Phone"></lightning-input-field>
<lightning-button type="submit" label="Save"></lightning-button>
</lightning-record-edit-form>
Create Record
- If the
record-id
attribute is omitted, the form switches to create mode, allowing you to insert a new record.
- If the
<lightning-record-edit-form
object-api-name="Contact"
onsuccess={handleSuccess}
>
<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 type="submit" label="Create"></lightning-button>
</lightning-record-edit-form>
Lightning Record View Form
<template>
<lightning-card title="Account Details" icon-name="standard:account">
<div class="slds-p-around_medium">
<lightning-record-view-form
object-api-name="Account"
record-id={recordId}>
<!-- Fields to display -->
<lightning-output-field field-name="Name"></lightning-output-field>
<lightning-output-field field-name="Phone"></lightning-output-field>
<lightning-output-field field-name="Website"></lightning-output-field>
<lightning-output-field field-name="Industry"></lightning-output-field>
</lightning-record-view-form>
</div>
</lightning-card>
</template>
import { LightningElement, api } from 'lwc';
export default class AccountViewForm extends LightningElement {
@api recordId; // Pass the record ID dynamically (e.g., from a parent component)
}
Lightning Record Form
The lightning-record-form
component in LWC is a versatile and declarative component that can handle view, edit, and create operations for a Salesforce record. It simplifies development by allowing you to quickly configure a form without writing a lot of boilerplate code. It’s an all-in-one solution compared to lightning-record-edit-form
and lightning-record-view-form
.
Key Features of lightning-record-form
:
Modes: Supports three modes:
View: To display a record in read-only mode.
Edit: To update an existing record.
Create: To create a new record.
Dynamic Fields: Can auto-generate fields using
layout-type
or define specific fields manually usingfields
.Simpler Setup: Automatically handles field rendering, validation, and layout generation based on the Salesforce object metadata.
<template>
//create mode
<lightning-record-form
mode="create"
object-api-name={objectApiName}
fields={fields}
onsuccess={handleSuccess}
>
</lightning-record-form>
// edit mode
<lightning-record-form
mode="edit"
record-id={recordId}
object-api-name={objectApiName}
fields={fields}
onsuccess={handleSuccess}
>
</lightning-record-form>
// view mode
<lightning-record-form
mode="readonly"
record-id={recordId}
object-api-name={objectApiName}
fields={fields}
columns="2"
>
</lightning-record-form>
</template>
import { api, LightningElement, track } from 'lwc';
import ACCOUNT_OBJECT from "@salesforce/schema/Account";
import ACCOUNT_NAME from "@salesforce/schema/Account.Name";
import ACCOUNT_INDUSTRY from "@salesforce/schema/Account.Industry";
/*
create
edit
view
*/
export default class RecordFormPractice extends LightningElement {
@api recordId;
objectApiName = ACCOUNT_OBJECT;
handleSuccess() {
alert('Data submitted');
}
// create record
// fields = [ACCOUNT_NAME];
// edit record
// fields = [ACCOUNT_NAME, ACCOUNT_INDUSTRY];
// view record
// fields = [ACCOUNT_NAME, ACCOUNT_INDUSTRY];
}
Wire adapters
In Lightning Web Components (LWC), a wire adapter is a special kind of adapter used to connect an LWC component to Salesforce data or services in a declarative manner.
It allows your component to retrieve data from Salesforce without having to manually write imperative JavaScript code for fetching the data.
The wire adapter is reactive, meaning it automatically updates the component whenever the data it is wired to changes.
In LWC wire adapters are designed to retrieve data declaratively and reactively, but they do not support operations like delete, update, or create directly. These operations require imperative code using Salesforce's Apex methods or UI API.
lightning/uiRecordApi
In Lightning Web Components (LWC), the uiRecordApi module provides a set of wire adapters and functions that enable you to work with Salesforce record data. This module allows you to easily retrieve, update, create, or delete record data without having to write complex Apex controllers or custom server-side logic.
There are few methods listed below
createRecord(recordInput):
// Configure your recordInput object with the object and field API names const fields = {} const recordInput = { apiName: Object API_NAME, fields }; try { // Invoke createRecord const account = await createRecord(recordInput); } catch (error) { // Handle error }
Example:
async handleSave() { // prepare contact data const fields = { AccountId: this.account, FirstName: this.firstName, LastName: this.lastName } const recordInput = {apiName: 'Contact', fields}; try { await createRecord(recordInput); this.showToast('Success', 'Contact created successfully', 'success'); } catch (error) { console.log("error while creating contact ", error); this.showToast('Error', 'Error creating contact ' + error.body.message, 'error') } }
deleteRecord(recordId)
const recordId = event.target.dataset.recordid; try { await deleteRecord(recordId); } catch (error) { // code here }
updateRecord(recordInput, clientOptions)
const fields = { Id: contactId, FirstName: this.firstName, LastName: this.lastName }; const recordInput = {fields}; const updateResult = await updateRecord(recordInput);
getRecords
@wire(getRecords, {recordId:'', fields: []}) propertyOrFunction
refreshApex
The parameter you refresh with
refreshApex()
must be an object that was previously emitted by an Apex@wire
.Sometimes, you know that the cache is stale. If the cache is stale, the component needs fresh data. To query the server for updated data and refresh the cache, import and call the
refreshApex()
function. InvokerefreshApex()
only when necessary because it incurs a network trip to query the server.
import { refreshApex } from "@salesforce/apex";
refreshApex(valueProvisionedByApexWireService);
Communication between LWC components
Parent to Child
Child to Parent
Component to Component (Pub/Sub)
Parent to child Communication
parentComponent.html
- In above code we have a child component name as
childComponent
we need to appendc
in order to call child component and name is the property which we are sending to child.
- In above code we have a child component name as
<template>
<lightning-input type="text" value={employeeName} onchange={handleEmployeeName}></lightning-input>
<c-child-component name={employeeName} onmessage={handleMessage}></c-child-component>
</template>
- childComponent.html
<template>
<h2>Hello {name}</h2>
</template>
- childComponent.js
import { track } from "lwc";
import { api, LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
@api name;
}
Child to Parent Communication
- Parent.html
<template>
<c-child-component onmessage={handleMessage}></c-child-component>
<div>Printing message from child {messageFromChild}</div>
</template>
- parent.js
import { LightningElement, track } from 'lwc';
export default class ParentComponent extends LightningElement {
handleMessage(event) {
// access the data sent via child in parent
this.messageFromChild = event.detail.data;
}
}
- child.html
<template>
<h2>Hello {name}</h2>
<lightning-button onclick={handleClick} label="Click for sending to parent"></lightning-button>
</template>
- child.js
import { track } from "lwc";
import { api, LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
handleClick() {
const event = new CustomEvent('message', {
detail: {data: 'Hello from child'}
});
this.dispatchEvent(event);
}
}
Component to Component (Pub/Sub)
- To communicate between 2 individual component we need pub/sub pattern. We can also implement using Lightning Messaging Service (LMS) which is preferred.
Example using Pub/Sub:
Create pubsub/anyname folder under LWC folder. This folder will contain pubsub file which has logic implmented for pubsub.
You can access exports of js file by importing them with help of
c/filename
Also create
filename.js-meta.xml
and add below code. This xml will contain info related to javascript file.pubsub.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
- pubsub.js
const events = {};
// subscribe to an event
const subscribe = (eventName, callback) => {
if (!events[eventName]) {
events[eventName] = [];
}
events[eventName].push(callback);
}
// publish an event
const publish = (eventName, payload) => {
if (events[eventName] && events[eventName].length > 0) {
events[eventName].forEach((callback) => callback(payload));
}
}
// unsubscribe an event
const unsubscribe = (eventName, callback) => {
if (events[eventName] && events[eventName].length > 0) {
events[eventName] = events[eventName].filter((currentCallback) => currentCallback !== callback);
}
}
export {
subscribe, publish, unsubscribe
}
- Component1PubSub.html
<template>
<lightning-card>
<lightning-button label="Publish Event" onclick={handlePublishEvent}></lightning-button>
</lightning-card>
</template>
- Component1PubSub.js
import { LightningElement } from 'lwc';
import {publish} from 'c/pubsub';
export default class Component1PubSub extends LightningElement {
handlePublishEvent() {
const payload = {data: 'Hello world'};
publish('messageEvent', payload);
}
}
- Component2PubSub.html
<template>
<lightning-card>
<div>
Message from component 1 {message}
</div>
</lightning-card>
</template>
- Component2PubSub.js
import { LightningElement, track } from 'lwc';
import {subscribe, unsubscribe} from 'c/pubsub';
export default class Component2PubSub extends LightningElement {
@track message = '';
connectedCallback() {
subscribe('messageEvent', (payload) => this.handleMessageEvent(payload));
}
disconnectedCallback() {
unsubscribe('messageEvent', (payload) => this.handleMessageEvent(payload));
}
handleMessageEvent(payload) {
console.log("Payload messgae", payload.data);
this.message = payload.data;
}
}
Lightning Messaging Service (LMS):
- LMS stands for Lightning Message Service. It's a versatile communication framework within Salesforce that enables seamless data exchange between different Lightning Web Components (LWCs) and other components on a Lightning page.
How to Use LMS in LWC:
Create a Lightning Message Channel:
Define a channel using the
@salesforce/messageChannel
module.Assign a unique name to the channel, typically with a
__c
suffix.
Publish a Message:
Import the
MessageContext
interface from@salesforce/messageChannel
.Obtain a reference to the message channel using the
MessageContext
object.Use the
publish()
method to send a message to the channel.
Subscribe to a Message:
Import the
subscribe()
method from@salesforce/messageChannel
.Obtain a reference to the message channel using the
MessageContext
object.Use the
subscribe()
method to listen for messages on the channel.Implement a callback function to handle the received messages.
Note :
You need to create and .xml file for passing event/eventName in
publish()
andsubscribe()
In XML master label tag will contain the eventname which will be eventually you file name below is the syntax.
Syntax :
YourEventName.messageChannel-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>MessageEvent</masterLabel>
<description>This Lightning Message Channel sends information from VF to LWC</description>
<isExposed>true</isExposed>
</LightningMessageChannel>
- Component1LMS.html
<template>
<lightning-card>
<lightning-button label="Publish Message" onclick={handlePublish}></lightning-button>
</lightning-card>
</template>
- Component1LMS.js
import { MessageContext, publish } from 'lightning/messageService';
import { LightningElement, wire } from 'lwc';
import MESSAGEEVENT from "@salesforce/messageChannel/MessageEvent__c"
export default class Component1LMS extends LightningElement {
@wire(MessageContext)
messageContext;
handlePublish() {
const payload = {data: 'Hey bro!'};
publish(this.messageContext, MESSAGEEVENT, payload);
}
}
- Component2LMS.html
<template>
<lightning-card>
Message from Parent is {message}
</lightning-card>
</template>
- Component2LMS.js
import { MessageContext, subscribe, unsubscribe } from 'lightning/messageService';
import { LightningElement, track, wire } from 'lwc';
import MESSAGEEVENT from "@salesforce/messageChannel/MessageEvent__c"
export default class Component2LMS extends LightningElement {
@wire(MessageContext)
messageContext;
@track message = '';
connectedCallback() {
subscribe(this.messageContext, MESSAGEEVENT, (payload) => this.handleEvent(payload));
}
disconnectedCallback() {
unsubscribe(this.handleEvent);
this.handleEvent = null;
}
handleEvent(payload) {
this.message = payload.data;
}
}
What is import MESSAGEEVENT from "@salesforce/messageChannel/MessageEvent__c"
- MESSAGEEVENT is a Message Channel passed in
subscribe()
andpublish()
method as event name.
MessageContext:
- The
MessageContext
object provides information about the Lightning web component that is using the Lightning message service. Get this object via theMessageContext
wire adapter or viacreateMessageContext()
.
publish(messageContext, messageChannel, message)
- Publishes a message to a specified message channel.
Parameter | Type | Description |
messageContext | object | The MessageContext object provides information about the Lightning web component that is using the Lightning message service. Get this object via the MessageContext wire adapter or via createMessageContext() . |
messageChannel | object | The message channel object. To import a message channel, use the scoped module @salesforce/messageChannel . To create a message channel in an org, use the LightningMessageChannel metadata type. |
message | object | A serializable JSON object containing the message published to subscribers. A message can't contain functions or symbols. |
subscribe(messageContext, messageChannel, listener, subscriberOptions)
- Subscribes to a specified message channel. Returns a Subscription object that you can use to unsubscribe.
Parameter | Type | Description |
messageContext | object | The MessageContext object provides information about the Lightning web component that is using the Lightning message service. |
messageChannel | object | To import a message channel, use the scoped module @salesforce/messageChannel . To create a message channel in an org, use the LightningMessageChannel metadata type. |
listener | function | A function that handles the message once it is published. |
subscriberOptions | object | (Optional) An object that, when set to {scope: APPLICATION_SCOPE} , specifies the ability to receive messages on a message channel from anywhere in the application. Import APPLICATION_SCOPE from lightning/messageService . |
unsubscribe(subscription)
- Unsubscribes from a message channel.
Parameter | Type | Description |
subscription | object | The Subscription object returned by the subscribe() function. |
What is Navigation in LWC
In Lightning Web Components (LWC), navigation is a fundamental aspect of creating dynamic and user-friendly applications. It involves seamlessly moving users between different pages, records, and components within your Salesforce org.
Key Concepts:
Navigation Service:
The
lightning/navigation
service provides the core functionality for navigating within your LWC applications.It uses
PageReference
objects to define the target destination.
PageReference:
A JavaScript object that encapsulates the details of the page to navigate to, including:
type
: Specifies the type of page (e.g.,standard__recordPage
,standard__objectPage
).attributes
: Defines specific parameters like record ID, object API name, and action name.state
: Optional state parameters to pass to the target page.
Core Navigation Methods:
[NavigationMixin.Navigate](pageReference, [replace])
:Navigates to the specified
PageReference
.The optional
replace
parameter determines whether to replace the current history entry or create a new one.
navigateToObjectHome() {
// Navigate to the Account home page
this[NavigationMixin.Navigate]({
type: 'standard__objectPage',
attributes: {
objectApiName: 'Account',
actionName: 'home',
},
});
}
[NavigationMixin.GenerateUrl](pageReference)
:
Generates a URL based on the given
PageReference
.Useful for creating links or opening URLs in new tabs or windows.
recordPageUrl;
connectedCallback() {
// Generate a URL to a User record page
this[NavigationMixin.GenerateUrl]({
type: 'standard__recordPage',
attributes: {
recordId: '005B0000001ptf1IAE',
actionName: 'view',
},
}).then((url) => {
this.recordPageUrl = url;
});
}
Extending LightningElement:
In LWC you extend LightningElement by default when creating a component, but when you want to use the NavigationMixin, you don’t directly extend LightningElement. Instead you use a decorator pattern where NavigationMixin is applied on top of LightningElement.
This is because LWC does not allow multiple inheritance.
How Does the NavigationMixin Work?
NavigationMixin
is a function which takes a base class as argument (usually LightningElement) and return a new Class with navigation functionality mixed in. By this approach , it wrapsLightningElement
to add new features.Example:
import {LightningElement} From 'lwc'; import {NavigationMixin} from 'lightning/navigation'; export default class YourComponent extends NavigationMixin(LightningElement) { // your component logic }