Salesforce Lightning Web Components Basics
Overview
Lightning Web Components (LWC) cannot be developed through the Developer Console, Visual Studio Code with the Salesforce Extension is recommended.
In VS Code, open the command palette and execute SFDX: Create Lightning Web Component. This will generate a folder under force-app/main/default/lwc. Each LWC folder and file is named in camelCase. If you start the name with an uppercase letter, it will automatically be changed to lowercase.
We will create a new component partnerSearch as an example. A new folder with the same name was created, which includes the following files:
partnerSearch.htmlpartnerSearch.jspartnerSearch.js-meta.xml
HTML Template
The partnerSearch.html file includes HTML tags, Base Components, and other custom LWC components.
<template>
html tags/other components
</template>
Naming convention
LWC components are named in camelCase, while references in HTML use standard HTML naming conventions. For example, partnerSearch.html is referenced as:
<c-partner-search></c-partner-search>
Data binding
Interaction between .html and .js is done through {property} binding for values and {function} binding for functions. For example:
<lightning-button onclick={handleClick} label={buttonLabel}></lightning-button>
Note that do not include quotes, exclamation marks, or spaces around the bindings. You do not need prefixes like c. or v. to distinguish between functions and values.
LWC Data Binding is one-way, changes to properties will update the template, but changes in the template will not update the property values unless you add a listener.
Directives
LWC has some built-in directives, like if:true, if:false, for:each, for:item, and key. For example,
Conditional Rendering
<div if:true={isDisplay}>some components</div>
<div if:false={isDisplay}>some else components</div>
For loop items
<template for:each={arrayProperty} for:item={array}></template>
LWC does not have equivalent attribute to aura:id, instead, it is recommended to use data-id to identify each tag/component.
Expressions
LWC does not support expressions like {!if(a=b, true, false)}. Use a property with a getter function instead:
get isDisplay() {
return a === b;
}
In an array, you can add a calculated value to the array property and calculate at the time of assignment.
LWC does not support global variables, everything displayed in the template must be in a property format.
Slots
A slot acts as a placeholder in a template, allowing tags/components from the caller to be inserted. For example,
parent.html
<template>
<c-slds-section>
<div> something </div>
</c-slds-section>
</template>
sldsSection.html
<template>
<section>
<slot></slot>
</section>
</template>
CSS
The partnerSearch.css file is not automatically generated when creating an LWC; you can manually create a corresponding .css file if needed. The CSS selector syntax is similar to Aura, but LWC does not prepend .THIS to each selector.
For some Base Components, custom styles are not supported (e.g., <lightning-button>). Classes applied to these components do not affect the internal button. Use <button> instead for custom styling.
For buttons inside other base components, like <lightning-data-table>, custom styling is not possible.
JavaScript
The partnerSearch.js file imports functions or objects from other modules. Each LWC imports LightningElement:
import { LightningElement } from 'lwc';
Define a class that extends LightningElement with custom properties and functions.
import { LightningElement } from 'lwc';
export default class PartnerSearch extends LightningElement {
searchName;
handleNameChange(event) {
this.searchName = event.detail.value;
}
searchPartners() {
const searchEvent = new CustomEvent('search', { detail: { 'name': this.searchName } });
this.dispatchEvent(searchEvent);
}
}
Modules
When referencing lwc modules and other custom modules, import is needed. Common imports includes,
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import { NavigationMixin } from 'lightning/navigation';
import { currentUserId } from '@salesforce/user/Id';
import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import TYPE_FIELD from '@salesforce/schema/Account.Type';
import resourceName from '@salesforce/resourceUrl/resourceName';
import apexMethod from '@salesforce/apex/apexcontroller/apexMethod';
Properties and Functions
Properties can be defined in two ways:
Directly
propertyName;
Using a getter function:
get propertyName() {
let name = someValue;
return name;
}
Properties should be named in camelCase.
Some common properties are automatically populated by LWC, such as
@api recordId: Automatically assigned the current record ID when set to RecordPage.@api flexipageRegionWidth: Indicates the width of the component, with values SMALL, MEDIUM, or LARGE.
Functions are defined directly:
functionName() {
}
DOM Manipulation
To get current elements or components, use this.template.querySelector() instead of document.querySelector(). During the execution of the .js code, document refers to the entire page, whereas .js operations should target the current component.
In Aura, component.find('auraId')
In LWC, this.template.querySelector('selector')
Decorators
Decorators are modifiers placed before property or function names. In LWC, there are three types: @api, @track, @wire. Import them before use
import { LightningElement, api, track, wire } from 'lwc';
@api
@api decorated properties or functions can be invoked from parent component. For example,
child.js
@api propertyName;
@api methodName(){
}
parent.html
<template>
<c-partner-search data-id="some-id" property-name="something"></c-partner-search>
</template>
parent.js
this.template.querySelector('[data-id="some-id"]').methodName();
@track
@track decorates the properties.
@track propertyName;
This indicates that the property is reactive, meaning that when its value changes, the HTML template updates accordingly. By default, all properties except those containing arrays or objects are reactive without needing @track.
Arrays or objects need to have @track decorated for reactivity.
@wire
@wire decorates properties or functions, binding them to Lightning Data Service wire adapters or Apex class methods.
For properties, the value is the return value from wire adapter or apex method. For functions, the parameter is the return value from wire adapter.
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import getPartners from '@salesforce/apex/PartnerController.getPartners';
// ...
@wire(getPartners, { accountId: '$recordId' })
partners;
@wire(getPartners, { accountId: '$recordId' })
wiredPartners({ error, data }) {
}
The @wire decorator accept two parameters, the first one is the name of wire adapter or apex method, the second one is the configuration of the wire adapter or parameters of the apex method.
The wire adapter is executed when the component initializes, and the data is cached on the client side, allowing it to be fetched from client side when needed.
When @wire is applied to a property or function, it subscribes to data updates in the backend. Any updates are automatically pushed to the property or function. If the HTML template references this property, it updates dynamically.
To call Apex methods with @wire, the Apex method must be annotated with @AuraEnabled(cacheable=true). These methods do not automatically monitor for updates, requiring a manual refresh by invoking refreshApex and passing the wired result as an argument.
The @wire decorator returns an object with {error, data}. If there is no error, the data attribute holds the value otherwise, the error information is in error.
Lightning Data Service
Lightning Data Service is based on UI API and facilitates object and record-level CRUD operations.
Common APIs
lightning/uiObjectInfoApi: For retrieving object information.lightning/uiRecordApi: For performing CRUD on records.
UI API supports most standard objects, except those like Event.
Importing Lightning Data Service creates a wire adapter, allowing easy binding to properties or functions within JavaScript code.
Calling Apex
Unlike Visualforce or Aura, LWC does not require an Apex controller. Importing an Apex method establishes a loose coupling between the Apex class and LWC.
There are two ways to call Apex, one is using @wire decorator as mentioned. Another is to call directly, it is very similar to the invocation in Aura or Visualforce remoting.
Import Apex method
import getPartner from "@salesforce/apex/namespace.classname.getPartner";
The imported function is a Promise that converted by LWC
getPartner({ params })
.then(result => {
})
.catch(error => {
});
Third-Party JavaScript Libraries
Third-Party libraries should be uploaded as Static Resources before being imported and loaded into components.
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import g6 from '@salesforce/resourceUrl/g6';
Load the library after rendering completed.
renderedCallback() {
Promise.all([
loadScript(this, g6 + '/g6.min.js')
]).then(() => { /* library loaded */ })
.catch(error => { /* handle error */ });
}
LWC discourages third-party DOM manipulation. If necessary, you can use the lwc:dom="manual" directive on elements to allow it.
Lightning Locker
Global objects like window, document, and elements are wrapped in secure counterparts SecureWindow, SecureDocument, SecureElement with restricted properties and methods.
LWC returns values often as Proxy objects, so using JSON.stringify() in developer console makes them easier to examine.
Apex-returned objects are immutable, to modify them, make a copy before applying any changes.
Lifecycle hooks
constructor(): Invoked when the component is created, typically used for initialization.connectedCallback(): Invoked when the component is inserted into the DOM.- Child components rendering: Runs lifecycle hooks for child components.
renderedCallback(): Invoked after the component is rendered, typically used for interacting with the rendered DOM.disconnectedCallback(): Invoked when the component is removed from the DOM.
Inter component communication
Parent to child communication can be implemented via @api decorator on properties and methods in child components.
Child to parent communication can be implemented via Custom Event.
child.js
const someEvent = new CustomEvent('something', {
detail: { params }
});
this.dispatchEvent(someEvent);
parent.js
this.template.querySelector('selector')
.addEventListener('something', this.doSomething);
or
parent.html
<template>
<c-child onsomething={doSomething}></c-child>
</template>
LWC can be embedded in Aura, and events are compatible.
Configurations
Metadata configuration (partnerSearch.js-meta.xml) determines where the component can be used, such as in tabs, record pages, or within the App Builder.
<template>
<c-child onsomething={doSomething}></c-child>
</template>
Aura and LWC comparison
- Performance: LWC loads components faster than Aura.
- Syntax: LWC is more intuitive and follows web standards.
- Code Structure: Aura’s client-side controllers and helpers provide structure but can be replaced with functions in LWC, which offers more flexibility.
- Use Aura as a wrapper for components where LWC is not supported, such as Quick Actions.