LearnFrenzy
Sign in Take a free test

Lightning Web Components (LWC) 19 min read

LWC Basic

Lightning Web Components basics — component files, HTML templates, JavaScript class, decorators and data binding — explained for beginners.

LWC Basic
On this page 8 sections
  1. 1. Passing values from controller to template
  2. 2. Passing values from template to controller
  3. JavaScript File (getterExample.js)
  4. HTML File (getterExample.html)
  5. 1. JavaScript File (forEachExample.js)
  6. 2. HTML Template File (forEachExample.html)
  7. 1. HTML Template (formValidation.html)
  8. 2. JavaScript Controller (formValidation.js)

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

<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:

  • 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

Two Way Binding

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;
    }
}

<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.

Output

@track decorator

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}`;
    }
}

<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:

Getter Example

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)

<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

  • 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

Getter Example

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>
    <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:

  • 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

Conditional Rendering

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>
  <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.

Output

Render List of item

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".

  1. for:each takes the array as an input
  2. for:item = ”currentItem” currentItem is the value of the current element. currentItem is an alias and can be anyname. for:item holds the currentItem.
  3. for:index = ”index” index is the current element index in the array. for:index holds the 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' }
  ];
}

Output

Render List of item

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>
  <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' }
  ];
}

Output

Render List of item-iterator

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>”

<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>

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

<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);
  }
}

Output

Access Element in DOM

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)

<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>

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

querySelector()

  • 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);
        });
    }
}

<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>

Output

querySelectorAll()

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

<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!

Written by

Saurabh Samir Technology & Learning Content Lead

Hey, my name is Saurabh Samir and I am Salesforce Developer. I have been helping to elevate your Lightning Web Components (LWC) Knowledge. Do comment below if you have any questions or feedback's.

View profile

Get new articles in your inbox

One short email when we publish something new — interview questions, Salesforce guides and career tips. No spam, unsubscribe in one click.

We will email you a link to confirm. Unsubscribe any time.

1 comment

Questions, corrections or your own experience — they all help the next reader.

  1. RAMANAMMA KEDARISETTI

    Nice explanation

Leave a comment

Comments are reviewed before they appear.

Search LearnFrenzy

Esc

Quick links