Decorators in Lightning Web Components | Salesforce Developer’s Guide

Salesforce offers Lightning Web Components (LWC) as a framework to swiftly create responsive and reusable web components on their platform. Among the notable features of LWC is its backing for decorators, employed to enhance the functionality of a component or its elements. This blog post delves into understanding decorators and their utilization within LWC.

What are Decorators

Decorators are a way to modify or enhance the behavior of a component or its members. In LWC, decorators are applied to class properties or methods using the @decoratorName syntax. Decorators can be used to add functionality like validation, caching, memorization, and event handling to a component or its members. 

Built-in Decorators:

LWC comes with several built-in decorators, which can be used to add common functionality to a component. Some of the most commonly used built-in decorators include: 

@track 

The @track decorator is used to mark a property as reactive. When a reactive property changes, the component is automatically re-rendered.

DecoratorLwc.js

import { LightningElement, track } from 'lwc'; 
export default class DecoratorLwc extends LightningElement { 
     @track welcomeMessage = {"message" : "Hey!"}; 
     showWelcomeMessage() 
     {
          this.welcomeMessage.message = 'Hey! Welcome to SalesforceBlue :)';
     } 
}

DecoratorLwc.html

<template> 
<lightning-card title="DecoratorLwc" >
<p class="slds-p-horizontal_small"> {welcomeMessage.message} </p> 
<lightning-button variant="brand" label="Click Me"  title="label" onclick={showWelcomeMessage} slot="actions">
</lightning-button>
</lightning-card> 
</template>

You can see the updated message being displayed, which wouldn’t be the case if we didn’t decorate the welcomeMessage property with @track.

Limitation of the track decorators

  1. You cannot expose these properties to the app builder.
  2. You cannot pass properties from other components.

@api

The @api decorator is used to mark a property or method as public and to expose it to other components.

apiDecoratorExampleParent.html

<template>
    <!-- Call child component -->
    <c-api-decorator-example-child greeting-message={greetingMessageFromParent}>
    </c-api-decorator-example-child>
</template>
apiDecoratorExampleParent.js
import { LightningElement } from 'lwc';
export default class ApiDecoratorExampleParent extends LightningElement {
    greetingMessageFromParent = 'Hello! This is an example for @api decorator';
}

apiDecoratorExampleChild.html

<template>
    <h2>{greetingMessage}</h2>
</template>
 apiDecoratorExampleChild.js
import { LightningElement, api } from 'lwc';
export default class ApiDecoratorExampleChild extends LightningElement {
    @api greetingMessage;
}

@wire

The @wire decorator is used to wire a component to a Salesforce Apex method or an external data source. When data changes, the component is automatically re-rendered. 

There are two ways we can use @wire to call apex:-

  1. Wire a property

This approach can be used when we do not want to process the data which is returned from the server(i.e., apex) and we just want to display it on the UI or store it in a component property for some other purpose. Below is sample code using the cable property in LWC. In this component, all child contact records related to the account are loaded and displayed in a table in the user interface.

wireDecoratorExample.html

<template>
    <!-- Display child contact records -->
    <table class="slds-table slds-table_bordered slds-border_left slds-border_right">
        <thead>
          <tr class="slds-line-height_reset">
            <th class="" scope="col">
              <div class="slds-truncate" title="Name">Name</div>
            </th>
            <th class="" scope="col">
              <div class="slds-truncate" title="Email">Email</div>
            </th>
            <th class="" scope="col">
                <div class="slds-truncate" title="Phone">Phone</div>
              </th>
          </tr>
        </thead>
        <tbody>
          <template for:each={listContact.data} for:item="contactRecord">
              <tr key={contactRecord.Id}>
                  <td>{contactRecord.Name}</td>
                  <td>{contactRecord.Email}</td>you 
                  <td>{contactRecord.Phone}</td>
              </tr>
          </template>
        </tbody>
      </table>
</template>
wireDecoratorExample.js
import { LightningElement, api, wire } from 'lwc';
import fetchChildContact from '@salesforce/apex/WireDecoratorExampleController.fetchChildContactRecords';
export default class WireDecoratorExample extends LightningElement {
    @api recordId;
    @wire(fetchChildContact, { accountId: '$recordId' })
    listContact;
}

2. Wire a function

This approach is used when there is a need to process the data that is returned from the apex. The returned data is provided to the callback function.

Let’s modify the above sample code to use a wired function instead of wired property

// wireDecoratorExample.html
<template>
    <!-- Display child contact records -->
    <table class="slds-table slds-table_bordered slds-border_left slds-border_right">
        <thead>
          <tr class="slds-line-height_reset">
            <th class="" scope="col">
              <div class="slds-truncate" title="Name">Name</div>
            </th>
            <th class="" scope="col">
              <div class="slds-truncate" title="Email">Email</div>
            </th>
            <th class="" scope="col">
                <div class="slds-truncate" title="Phone">Phone</div>
              </th>
          </tr>
        </thead>
        <tbody>
          <template for:each={listContact} for:item="contactRecord">
              <tr key={contactRecord.Id}>
                  <td>{contactRecord.Name}</td>
                  <td>{contactRecord.Email}</td>
                  <td>{contactRecord.Phone}</td>
              </tr>
          </template>
        </tbody>
      </table>
</template>
// wireDecoratorExample.js
import { LightningElement, api, wire } from 'lwc';
import fetchChildContact from '@salesforce/apex/WireDecoratorExampleController.fetchChildContactRecords';
export default class WireDecoratorExample extends LightningElement {
    @api recordId;
    listContact;
    @wire(fetchChildContact, { accountId: '$recordId' })
    wiredContacts({ error, data }) {
        if (data) {
            this.listContact = data;
        } else if (error) {
            this.listContact = undefined;
        }
    }
}

Customized Decorators: Beyond the pre-existing options, developers possess the ability to craft their custom decorators, augmenting distinct functionalities within their components. For instance, a developer might fashion a tailored decorator to manage data caching or incorporate event handling into a component.

To craft a custom decorator, developers define a function taking a target object (the class) and a property key (identifying the property or method being decorated) as parameters. Subsequently, the function can alter the behavior of the property or method according to requirements.

In Summary: Decorators represent a potent facet of LWC, enabling developers to imbue components and their elements with functionality in a reusable and adaptable manner. Leveraging both built-in decorators and personalized ones, developers can create components that are more agile, resourceful, and simpler to maintain.