LWC Basic
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
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:
1. Passing values from controller to template:
- In the JavaScript controller, define properties that hold the data you want to pass to the template.
- These properties are marked with the `@track` decorator to make them reactive, meaning any changes to these properties trigger re-renders in the template.
- In the HTML template, use curly braces `{{ }}` to reference these properties and display their values.
2. Passing values from template to controller:
- In the HTML template, use input fields or other interactive elements to allow users to input data or make changes.
- Use event handlers like `onchange` or `oninput` to detect changes in these input fields.
- When a change occurs, the updated value is automatically bound to the corresponding property in the controller, thanks to two-way binding.
Example:
<lightning-input label="Enter your name" value={name} onchange={handleChange}></lightning-input>Hello, {name}!
// 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:
- The `name` property in the controller is marked with `@track`, making it reactive.
- The `lightning-input` field in the template binds to the `name` property using two-way binding.
- Any changes made in the input field are automatically reflected in the `name` property, and vice versa.
Output:
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.
@Track
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; } }
<lightning-input label="Enter First Name" value={fullName.firstName} onchange={handleChange}> </lightning-input>Hello, {fullName.firstName} {fullName.lastName}!
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.
Output:
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.
Getter
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}`; } }
Full Name: {fullName}
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:
JavaScript File (getterExample.js):
// 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]; } }
HTML File (getterExample.html):
Addition of {num1} and {num2} is {result}
Full Name: {fullName}
Explanation:
- The component computes the result of adding `num1` and `num2` using the getter method `result`.
- It also computes the full name by concatenating the elements of the `arr` array using the getter method `fullName`.
- In the HTML template, we display both the computed result and full name within the `lightning-card` component.
- Whenever `num1`, `num2`, or `arr` change, the computed values will automatically update in the template due to the reactivity of getters.
Output:
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
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 if:true={tasksAvailable}>Tasks are available. Please review.
</template> <template if:false={tasksAvailable}>No tasks available at the moment.
</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:
- The `taskList` component receives a list of tasks through the `tasks` property.
- We use a getter named `tasksAvailable` to check if there are any tasks in the list.
- Conditional rendering is performed based on the value returned by the `tasksAvailable` getter.
- If tasks exist, we display a message indicating their presence; otherwise, we display a message indicating no tasks are available.
This scenario demonstrates how conditional rendering can be used to dynamically display different content based on certain conditions, providing a more personalized user experience.
Output:
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.
Render List of item
for:each
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:
1. JavaScript File (forEachExample.js):
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' } ]; }
2. HTML Template File (forEachExample.html):
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 for:each={items} for:item="item" for:index="index">Item {item.id}: {item.name}
</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.
Output:
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"
.
- for:each takes the array as an input
- for:item = ”currentItem” currentItem is the value of the current element. currentItem is an alias and can be anyname. for:item holds the currentItem.
- for:index = ”index” index is the current element index in the array. for:index holds the index.
Example:
<template for:each={nameArray} for:item="eachItem" for:index="forIndex"> <p class="sids-p-horizontal_small" key={i}> {eachItem} </p> </template>
<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>
//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' } ]; }
Output:
iterator
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.
For example, `{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 iterator:it={arrOfObj}> <p class="slds-p-horizontal_small" key={it.value.id}> <template lwc:if={it.first}> iterator "first" property running
</template>
{it.value.fName} {it.value.lName} </template> <template lwc:if={it.last}> iterator "last" property running
{it.value.fName} {it.value.lName} </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' } ]; }
Output:
Component Composition
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>”
‹strong>This is parent component
Access Element in DOM
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.
querySelector()
Example:
Access element<lightning-button variant="brand" label="go to console" title="Primary action" onclick={clickHandler}> </lightning-button>
//accessElementInComponent import { LightningElement } from 'lwc'; export default class AccessElementInComponent extends LightningElement { clickHandler(){ const paragraphElement = this.template.querySelector ('b') console.log(paragraphElement.innerText); } }
Output:
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()`:
1. HTML Template (formValidation.html):
<lightning-button label="Submit" onclick={handleSubmit}></lightning-button>
2. JavaScript Controller (formValidation.js):
//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); } }
Output:
- If the user tries to submit the form without filling in all fields, an error message will be displayed.
- If all fields are filled, a success message will be shown and the form will be submitted.
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.
querySelectorAll()
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); }); } }
Element 1Element 2Element 3<lightning-button label="Fetch Elements" onclick={handleButtonClick}> </lightning-button>
Output:
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
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:
//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:
<lightning-button label="Submit" onclick={handleSubmit}> </lightning-button>
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!
(1) Comments
Nice explanation