Understanding Decorators and Lifecycle Hooks in Salesforce LWC
Understand LWC decorators (@api, @track, @wire) and lifecycle hooks with clear examples — what each does and when to use it in your components.
Lightning Web Components basics — component files, HTML templates, JavaScript class, decorators and data binding — explained for beginners.
Welcome to the world of Lightning Web Components (LWC), the modern framework for building dynamic and interactive user interfaces on the Salesforce platform. In this beginner's guide, we'll explore the basic functionalities of LWC and provide hands-on examples to help you kickstart your journey as an LWC developer.
In this topic we will cover some basic functionality of Lightning Web Component (LWC)
Two way binding used to pass value from controller to template, and if there is any changes in template, it pass back to controller and update the value.
After Spring 20, by default all fields in JS are treated as reactive.
Here's how two-way binding works in LWC:
@track decorator to make them reactive, meaning any changes to these properties trigger re-renders in the template.{{ }} to reference these properties and display their values.onchange or oninput to detect changes in these input fields.Example
<template>
<lightning-card title="Two-Way Binding Example">
<div class="slds-p-around_medium">
<lightning-input label="Enter your name" value={name} onchange={handleChange}></lightning-input>
<p>Hello, {name}!</p>
</div>
</lightning-card>
</template>
// twoWayBindingExample.js
import { LightningElement, track } from 'lwc';
export default class TwoWayBindingExample extends LightningElement {
@track name = '';
handleChange(event) {
this.name = event.target.value;
}
}
In this example:
name property in the controller is marked with @track, making it reactive.lightning-input field in the template binds to the name property using two-way binding.name property, and vice versa.As you type in the input field, the value is updated in real-time in the UI and the name property of the component's data model.
In Lightning Web Components (LWC), the @track decorator is used to indicate that a property should be reactive, meaning changes to its value will be observed by the framework and cause the component to re-render when necessary.
When you assign an object or an array to a field in LWC, the framework doesn't automatically detect changes to the internal properties or elements of that object or array. However, if you want the framework to observe changes to these internal properties or elements, such as when you assign a new value or modify an existing one, you need to explicitly specify it using the @track decorator.
By decorating a field with @track, you're telling the LWC framework to track changes to that field's value and re-render the component whenever it detects such changes. This ensures that any updates made to the properties or elements of the object or array will be reflected in the component's UI.
Example
// trackExample.js
import { LightningElement, track } from 'lwc';
export default class TrackExample extends LightningElement {
@track fullName = { firstName: 'Saurabh', lastName: 'Samir' };
handleChange(event) {
this.fullName.firstName = event.target.value;
}
}
<template>
<lightning-card title="Track Example">
<div class="slds-p-around_medium">
<lightning-input label="Enter First Name"
value={fullName.firstName}
onchange={handleChange}>
</lightning-input>
<p>Hello, {fullName.firstName} {fullName.lastName}!</p>
</div>
</lightning-card>
</template>
In this example, the fullName object is decorated with @track, indicating that changes to its properties (such as firstName and lastName) should be tracked by the framework. When the firstName property is modified in the handleChange method, the component will re-render to reflect the updated value in the UI.
As the user enters a new first name in the input field, the component will re-render to reflect the updated value in the greeting message.
In Lightning Web Components (LWC), getters are used to compute or derive a value based on other properties or variables, and then make that computed value available for use in the component's template.
Lets say you have some property in JS and if you want to execute some expression it’s not allowed in LWC, to overcome with situation we use “Getter”. In Getter we perform some JS operation and it will binding into our html template. Getter always something “Return”.
A getter method is a special type of method that is prefixed with the keyword
get. Inside the getter method, you can perform any JavaScript operations or calculations needed to derive the desired value. This value is then returned by the getter method.
Once defined, the getter method becomes a reactive property, meaning changes to its dependent properties will trigger a re-evaluation of the getter method, ensuring that the computed value stays up-to-date.
Example-1
// getterExample.js
import { LightningElement, track } from 'lwc';
export default class GetterExample extends LightningElement {
@track firstName = 'Saurabh';
@track lastName = 'Samir';
// Getter method to compute the full name
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
<template>
<lightning-card title="Getter Example">
<div class="slds-p-around_medium">
<p>Full Name: {fullName}</p>
</div>
</lightning-card>
</template>
In this example, we have defined a getter method named fullName that returns the concatenated value of the firstName and lastName properties. Whenever either firstName or lastName changes, the getter method is automatically re-evaluated, ensuring that the computed fullName property stays in sync with its dependent properties.
The computed fullName property can then be used in the component's template to display the full name.
Output
Assuming the firstName is "Saurabh" and the lastName is "Samir" initially, the output will initially show:
If you were to change the values of firstName or lastName, the displayed full name would automatically update accordingly due to the reactivity of the getter method.
Example-2
// getterExample.js
import { LightningElement, track } from 'lwc';
export default class GetterExample extends LightningElement {
// Define some properties
num1 = 10;
num2 = 20;
arr = ['Saurabh', 'Samir'];
// Getter method to compute the result of adding num1 and num2
get result() {
return this.num1 + this.num2;
}
// Getter method to compute the full name from the array
get fullName() {
return this.arr[0] + ' ' + this.arr[1];
}
}
<template>
<lightning-card title="Getter Example">
<div class="slds-p-around_medium">
<p slot="footer">
Addition of {num1} and {num2} is {result}
Full Name: {fullName}
</p>
</div>
</lightning-card>
</template>
Explanation
num1 and num2 using the getter method result.arr array using the getter method fullName.lightning-card component.num1, num2, or arr change, the computed values will automatically update in the template due to the reactivity of getters.This output is dynamically generated based on the values of num1, num2, and arr, and it will update automatically if any of these values change due to the reactive nature of Lightning Web Components.
Conditional rendering in Lightning Web Components allows you to conditionally display or hide elements in the HTML template based on certain conditions.
Mind it!
The if:true and if:false directives were used in older versions of LWC for conditional rendering. However, with the introduction of LWC directives like lwc:if, lwc:elseif, and lwc:else, more advanced conditional rendering capabilities became available.
Here's how you can use these directives:
lwc:if: Renders the element if the expression evaluates to true.lwc:elseif: Renders the element if the preceding lwc:if or lwc:elseif condition(s) evaluate to false and this condition evaluates to true.lwc:else: Renders the element if none of the preceding lwc:if or lwc:elseif conditions evaluate to true.These directives allow you to write more expressive and concise conditional rendering logic in your Lightning Web Components.
Example
Let's consider a scenario where you have a Lightning Web Component that displays a list of tasks. However, you want to display different messages based on the number of tasks in the list. Here's how you can achieve this with conditional rendering:
In the HTML template:
<template>
<lightning-card title="Tasks">
<div class="slds-p-horizontal_small">
<template if:true={tasksAvailable}>
<p>Tasks are available. Please review.</p>
</template>
<template if:false={tasksAvailable}>
<p>No tasks available at the moment.</p>
</template>
</div>
</lightning-card>
</template>
In the JavaScript file:
// taskList.js
import { LightningElement } from 'lwc';
export default class TaskList extends LightningElement {
tasks = []; // Placeholder for tasks data
// Method to check if tasks are available
get tasksAvailable() {
return this.tasks.length > 0;
}
}
In this scenario:
taskList component receives a list of tasks through the tasks property.tasksAvailable to check if there are any tasks in the list.tasksAvailable getter.This scenario demonstrates how conditional rendering can be used to dynamically display different content based on certain conditions, providing a more personalized user experience.
The output dynamically changes based on the presence or absence of tasks in the list. If tasks exist, the component displays a message prompting the user to review them. If no tasks are present, it informs the user that there are none available at the moment.
Rendering a list of items in Lightning Web Components (LWC) using the for:each directive allows you to iterate over an array of data and render elements dynamically based on each item in the array. Here's how you can achieve this:
In your JavaScript file, define an array property that holds the data you want to render in the list. This array can contain objects representing individual items with their respective properties.
// forEachExample.js
import { LightningElement } from 'lwc';
export default class ForEachExample extends LightningElement {
// Array of items to render in the list
items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];
}
In your HTML template, use the for:each directive to iterate over the array of items. Within the directive, use the key attribute to specify a unique identifier for each item and define the template block that represents each item.
<template>
<lightning-card title="List of Items">
<div class="slds-p-horizontal_small">
<template for:each={items}
for:item="item"
for:index="index">
<p key="{item.id}">Item {item.id}: {item.name}</p>
</template>
</div>
</lightning-card>
</template>
With this setup, each item in the items array will be rendered dynamically as a paragraph (<p>) element within the Lightning card. The for:each directive automatically iterates over the array, creating a new instance of the template block for each item, and binds the item data to the template variables (item in this case). The key attribute ensures efficient DOM rendering and helps in identifying each item uniquely.
Each item in the array will be displayed in a separate paragraph within the Lightning card, with its ID and name shown. The for:each directive iterates over the items array, creating a paragraph for each item, and dynamically binds the data to the template for rendering.
Mind it!
When using the for:each directive, use for:item="currentItem" to access the current item. In the below example doesn’t use it, but to access the current item’s index, use for:index="index".
Example
<template>
<lightning-card title="for:each Loop in LWC">
<p slot="footer">
<template for:each={nameArray}
for:item="eachItem"
for:index="forIndex">
<p class="sids-p-horizontal_small" key={i}>
{eachItem}
</p>
</template>
</p>
</lightning-card>
<lightning-card title="for:each Loop for array of object">
<p slot="footer">
<template for:each={arrOfObj}
for:item="eachItem"
for:index="forIndex">
<p class="slds-p-horizontal_small" key={eachItem.id}>
{eachItem. fName} {eachItem. lName}
</p>
</template>
</p>
</lightning-card>
</template>
//lwcLooping.js
import { LightningElement } from 'lwc';
export default class LwcLooping extends LightningElement {
nameArray = ['Saurabh', 'Sara', 'Joy']
//arrays of objects that
arrOfObj = [
{ id: 1, fName: 'Saurabh', lName: 'Samir'},
{ id: 2, fName: 'Sara', lName: 'Khettab' },
{ id: 3, fName: 'Joy', lName: 'Purkait' }
];
}
To apply a special behavior to the first or last item in a list, use the iterator directive, iterator:iteratorName={array}. Use the iterator directive on a template tag.
Use iteratorName to access these properties:
value : The value of the item in the list. Use this property to access the properties of the array. {iteratorName}.value.{propertyName}.index : The index of the item in the list.first : A boolean value indicating whether this item is the first item in the list.last : A boolean value indicating whether this item is the last item in the list.Example
<template>
<lightning-card title="Iterator in LWC">
<p slot="footer">
<template iterator:it={arrOfObj}>
<p class="slds-p-horizontal_small" key={it.value.id}>
<template lwc:if={it.first}>
iterator "first" property running
{it.value.fName} {it.value.lName}
</template>
<template lwc:if={it.last}>
iterator "last" property running
{it.value.fName} {it.value.lName}
</template>
</p>
</template>
<p></p>
</lightning-card>
</template>
//lwcLooping.js
import { LightningElement } from 'lwc';
export default class LwcLooping extends LightningElement {
//arrays of objects
arrOfObj = [
{ id: 1, fName: 'Saurabh', lName: 'Samir'},
{ id: 2, fName: 'Sara', lName: 'Khettab' },
{ id: 3, fName: 'Joy', lName: 'Purkait' }
];
}
It’s useful to compose apps and components with a set of smaller components to make the code more reusable and maintainable.
Adding component within the body of another component. when you wanna embedded child component into parent component then we use component composition.
Syntax:- childComponent = “<c-child-component> </c-child-component>”
<template>
<lightning-card title="Component composition">
<p class="slds-p-horizontal_small">
‹strong>This is parent component
</p>
<p slot="footer">
<c-child-component></c-child-component>
</p>
</lightning-card>
</template>
To access elements rendered by a Lightning Web Component (LWC) in the DOM, you can use standard DOM APIs like querySelector() with this.template or this. Additionally, you can use the refs attribute to reference elements directly in the template.
Here's an example of how you can access elements in the DOM using both methods:
// Accessing elements using querySelector() with this.template
const element = this.template.querySelector('.example-class');
// Accessing elements using refs
this.template.querySelector('lightning-input').value = 'New Value';
In the above code:
this.template.querySelector('.example-class') searches for an element with the class example-class within the template.this.template.querySelector('lightning-input') accesses a Lightning Web Component with the tag <lightning-input> and sets its value to 'New Value'.Additionally, if you want to dynamically set attributes, you can use the setAttribute() method:
element.setAttribute('attribute-name', 'attribute-value');
This method allows you to add attributes dynamically to elements in the DOM.
Example
<template>
<lightning-card title="Access element in DOM">
<div class="slds-m-horizontal_x-small">
Access element
</div>
<p class="slds-p-horizontal_small">
<lightning-button variant="brand"
label="go to console"
title="Primary action"
onclick={clickHandler}>
</lightning-button>
</p>
</lightning-card>
</template>
//accessElementInComponent
import { LightningElement } from 'lwc';
export default class AccessElementInComponent extends LightningElement {
clickHandler(){
const paragraphElement = this.template.querySelector ('b')
console.log(paragraphElement.innerText);
}
}
In a real-world scenario, let's consider a Lightning Web Component (LWC) that contains a form with multiple input fields. The requirement is to validate these input fields before submitting the form.
Here's how you can implement this using querySelector():
<template>
<lightning-card title="Form Validation">
<div class="slds-p-around_medium">
<lightning-input label="Name" data-id="nameField"></lightning-input>
<lightning-input label="Email" data-id="emailField"></lightning-input>
<lightning-button label="Submit" onclick={handleSubmit}></lightning-button>
</div>
</lightning-card>
</template>
//formValidation.js
import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class FormValidation extends LightningElement {
handleSubmit() {
// Accessing input fields using querySelector()
const nameInput = this.template.querySelector('[data-id="nameField"]');
const emailInput = this.template.querySelector('[data-id="emailField"]');
// Validating input fields
if (!nameInput.value.trim() || !emailInput.value.trim()) {
// Display error message
this.showToast('Error', 'Please fill in all fields.', 'error');
} else {
// Form submission logic
this.showToast('Success', 'Form submitted successfully.', 'success');
}
}
showToast(title, message, variant) {
const event = new ShowToastEvent({
title: title,
message: message,
variant: variant
});
this.dispatchEvent(event);
}
}
In this scenario, querySelector() is used to access the input fields by their custom data attributes (data-id). This allows for flexible and efficient DOM manipulation, enabling the validation of form fields before submission.
If you have multiple elements then you can use querySelectorAll() method to fetch all elements.
remember: querySelectorAll() always return nodeList, to convert into Array form, use Array.from()
Here's a real-time example demonstrating the use of querySelectorAll() with code and output:
Example
// querySelectorExample.js
import { LightningElement } from 'lwc';
export default class QuerySelectorExample extends LightningElement {
handleButtonClick() {
// Select all elements with the class 'custom-element'
const elements = this.template.querySelectorAll('.custom-element');
// Convert the NodeList to an Array using Array.from()
const elementsArray = Array.from(elements);
// Log the number of elements found
console.log('Number of elements:', elementsArray.length);
// Loop through each element and log its text content
elementsArray.forEach((element, index) => {
console.log(`Element ${index + 1}:`, element.textContent);
});
}
}
<template>
<lightning-card title="Form Component">
<div class="slds-p-around_medium">
<div class="custom-element">Element 1</div>
<div class="custom-element">Element 2</div>
<div class="custom-element">Element 3</div>
<lightning-button label="Fetch Elements"
onclick={handleButtonClick}>
</lightning-button>
</div>
</lightning-card>
</template>
In this example, we have three <div> elements with the class custom-element. When the button is clicked, the handleButtonClick() method is invoked. Inside this method, querySelectorAll('.custom-element') selects all elements with the class custom-element. We then convert the resulting NodeList to an array using Array.from() and log each element's text content.
Refs locate DOM elements without a selector and only query elements contained in a specified template. First, add the lwc:ref directive to your element and assign it a value. To call that reference, use this.refs.
Example
<template>
<div lwc:ref="myDiv"></div>
</template>
//accessElementInComponent.js
import { LightningElement } from 'lwc';
export default class AccessElementInComponent extends LightningElement {
renderedCallback(){
console.log(this.refs.myDiv);
}
}
Mind it!
Using the lwc:ref directive in Lightning Web Components (LWC) allows you to reference a specific HTML element within your component's template. This is particularly useful when you need to interact with that element in your JavaScript code.
Here's an example scenario where lwc:ref can be used effectively:
Let's say you have a Lightning Web Component that displays a form, and you want to validate user input when the form is submitted. You need to access the input fields in the form to retrieve their values and perform validation checks.
Here's how you can achieve this using lwc:ref:
formComponent.html
<template>
<lightning-card title="Form Component">
<div class="slds-p-around_medium">
<lightning-input label="First Name" type="text" required="" lwc:ref="firstNameInput">
</lightning-input>
<lightning-input label="Last Name" type="text" required="" lwc:ref="lastNameInput">
</lightning-input>
<lightning-button label="Submit"
onclick={handleSubmit}>
</lightning-button>
</div>
</lightning-card>
</template>
formComponent.js
// formComponent.js.js
import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class FormComponent extends LightningElement {
handleSubmit() {
// Access the input field values using refs
const firstNameInput = this.template.querySelector('[data-ref="firstNameInput"]');
const lastNameInput = this.template.querySelector('[data-ref="lastNameInput"]');
// Perform validation checks
if (!firstNameInput.value || !lastNameInput.value) {
// Display an error message if any field is empty
this.showToast('Error', 'Please fill in all fields.', 'error');
return;
}
// If validation passes, proceed with form submission
// Code to submit the form data goes here
}
showToast(title, message, variant) {
const event = new ShowToastEvent({
title: title,
message: message,
variant: variant
});
this.dispatchEvent(event);
}
}
In this example, lwc:ref is used to reference the input fields for first name and last name. In the JavaScript code, this.template.querySelector() is used to access these input fields by their ref values. This allows you to retrieve their values and perform validation checks before submitting the form.
Using lwc:ref in this way enhances the modularity and maintainability of your Lightning Web Components by allowing you to easily access and manipulate specific elements within their templates.
“Stay tuned for more exciting LWC topics, and keep exploring the world of Lightning Web Components to become a Salesforce development pro.”
Happy coding with LWC!
One short email when we publish something new — interview questions, Salesforce guides and career tips. No spam, unsubscribe in one click.
Understand LWC decorators (@api, @track, @wire) and lifecycle hooks with clear examples — what each does and when to use it in your components.
JavaScript fundamentals for Lightning Web Components — scope, this, closures, promises, events and modules — explained with LWC-focused examples.
The JavaScript concepts to know before starting Lightning Web Components — variables, functions, objects, arrays, classes, modules and promises.
Questions, corrections or your own experience — they all help the next reader.
RAMANAMMA KEDARISETTI