Need to Learn How to Build Lightning Web Components?

Today my course about Getting Started with Salesforce Lightning Web Components was published on Pluralsight. This is my 14th course for Pluralsight. This not a tutorial. Instead, I tried to to explain why things were done the way they were and provide tips for how you can do the same when building your own solutions.

10 minute preview video

If you do not have a subscription to Pluralsight, you can always sign up for a free 10-day trial here. I have also created a GitHub repo that contains all the code for the course. Here you will find detailed instructions for how to install this in your own scratch org.

Easily Prototyping a Nested Salesforce Lightning Web Component

This post is in preparation for my soon to be released Pluralsight course, Getting Started with Salesforce Lightning Web Components. While creating that course, I realized how super valuable a tool called webcomponents.dev can be for prototyping an app using Lightning web components (LWC’s).

I created a sample app using their free online IDE. This prototype uses the scaffolding provided for a basic LWC app, with nested components. I am using JSON data to render a list of product tiles. My favorite reasons to use this tool:

  1. See preview of app immediately – In VS Code, you have to configure your meta config file to expose the component, push/deploy to an org and then open that org to see the results. Best of all, you can see it on different screen sizes like tablet and phone.
  2. Integration with GitHub – Create a new repo and then create a branch to make changes.
  3. Share results with others – You can share with a URL (as I have done in this post) or by posting right to Twitter. Share as either a simple view or in play mode where the user is not required to fork for tinkering with the code.

This tool was selected by Salesforce to replace the old playground feature, that has been deprecated. In all fairness, this tool is so much better than the playground. You can use it to create more than just LWC apps. It supports JavaScript, TypeScript, React, Vue 3, etc.

All that being said, this is NOT a replacement for the Salesforce CLI or the Salesforce Extensions for VS Code.

All that being said, this is NOT a replacement for the Salesforce CLI or the Salesforce Extensions for VS Code. I would just use this for prototyping an app quickly or testing out a LWC base component.

Enjoy and let me know what you think.

Workaround for Issue Debugging LWC in Chrome Dev Tools After Winter 2023

 🤯 This weekend, I discovered an issue trying to debug a Lightning Web Component in the Chrome debugger tools.

UPDATE on 11/13/2022: Salesforce includes a way to automatically disable LWS for your scratch orgs through the security settings in your config/project-scratch-def.json file. You will simply need to add a sessionSetting for lockerServiceNext set to false. Instructions below show you how to do this through Setup. Thanks to Grzegorz Skaruz for pointing this out.

Sharing this, in case anyone becomes as baffled and frustrated as I was. The issue involves the fact that Lightning Web Security (LWS) was installed by default in all orgs with Winter 23. I am not bashing LWS, because I think it is cool and very much needed.

But, for scratch orgs? Not so much.

If you try to debug JavaScript in a scratch org using the Chrome Debugger, you will no longer see the components listed in the page hierarchy under the Sources tab (like you may be used to doing).

To still debug your LWC’s, you will need to first disable this feature in your scratch org. You can do this in:

Session Settings -> Uncheck “Use Lightning Web Security for Lightning Web Components”. Click Save.

Unfortunately, it is still hard to see where to go in Chrome Debugger. But, you expand the c subfolder under components (see image below).

Chrome Debugger After LWC Disabled in Scratch Org

Good luck and if you find a better way to do this, please leave a comment below to help others.

Top Five Best Practices for Lightning Web Components

Top five best practices for Lightning Web Components (LWC)

👏🏽 The following best practices are in order of importance.

# 1 – Always Cache Data

Lightning web components offer two ways to cache data. The first is to use Lightning Data Service, which automatically handles security for you. The best part is that you do not have to write any Apex classes – especially platform required test classes.

If you must use Apex, then you can simply mark your methods as cacheable using the AuraEnabled annotation. The Winter 23 release introduced the global scope option for this annotation.

@AuraEnabled(scope=global)
public static myCacheableMethod() {}

# 2 – Use Conditional Rendering

Conditional rendering means that portions of a component will not be rendered until a condition is met.

For example, let’s assume you had a component that displayed a list of widget data. You would not want the list to be built unless there was data available. If there was no data, then the user would see a message telling them there are no widgets.

<div if:true{widgets}>
   <template for:each={widgets} for:item="widget">
       <li key={widget.Id}>{widget.Name}</li>
   </template>
</div>
<div if:false{widgets.length}>
   There are no widgets available
</div>

#3 – Use Pagination with Lists

While we are on the topic of lists, rendering a list of data has the potential for causing lots of performance problems. Many Salesforce orgs have custom objects that contain hundreds, thousands, if not millions of records.

The best way to prevent these lists from getting out of control is to introduce a pagination component. You can see an example of this in the eBikes GitHub repository.

<c-paginator
   page-number={pageNumber}
   page-size={products.data.pageSize}
   total-item-count={products.data.totalItemCount}
   onprevious={handlePreviousPage}
   onnext={handleNextPage}>
</c-paginator>

# 4 – Use Base Lightning Web Components

There are now 94 base Lightning web components to choose from. They cover everything from a simple input box to a complex record form.

These components not only offer the CSS from the Salesforce Lightning Design System (SLDS), but they offer a performance advantage. These components are already rendered at the client-side, so they do not require additional download processing.

# 5 – Use SLDS Icons and Styling

And since I mentioned the SLDS, let me remind you that the Lightning Design System website offers hundred of optimized icons. Using your own customized icons can result is low render quality and resolution, so be sure to take advantage to these readily available goodies.

Want to learn more about Lightning Web Components?

Check out my Building your First Lightning Web Component (LWC) course

(There is a new Getting Started course due to be published next month)

Post 3 – Communicate Between Unrelated Salesforce Lightning Web Components

This is the latest series of posts I am doing on a new Pluralsight course I created titled, “Communicate Between Salesforce Lightning Web Components”. You can also check out a free 10 minute preview video that explains how I got to this point in the post series.

Welcome Back

In the last post, I showed you a solution to display products, but only focused on how parent to child communication worked in that example. In this post, I will point out the portions of the code that involve working with an unrelated component and show you the unrelated component that displayProducts will be working with.

Just to refresh your memory, the code for displayProducts is as follows:

<!-- displayProduct.html -->

<template>
    <div class="slds-card slds-var-p-around_x-small">
        <lightning-input
            label="Search Key"
            type="text"
            onchange={handleSearchKeyChange}
            class="search-bar"
        ></lightning-input>
        <template if:true={products.data}>
          <template if:true={products.data.length}>
             <div class="content">
                <template for:each={products.data}
                     for:item="product">
                     <!-- Product Tile Component here-->
                     <c-product-tile
                        key={product.Id}
                        product={product}
                        onselected={handleProductSelected}
                        class="slds-var-m-around_x-small">
                     </c-product-tile>
                 </template>
              </div>
            </template>
            <template if:false={products.data.length}>
                <p>No products matching your selection</p>
            </template>
        </template>
        <template if:true={products.error}>
            <p>Error when getting product data</p>
        </template>
    </div>
</template>


// displayProducts.js

import { LightningElement, wire } from 'lwc';

import { publish, MessageContext } from 'lightning/messageService';
import PRODUCT_SELECTED_MESSAGE from '@salesforce/messageChannel/ProductSelected__c';

// getProducts() method in ProductController Apex class
import getProducts from '@salesforce/apex/ProductController.getProducts';

export default class DisplayProducts extends LightningElement {

    searchKey = '';     
    
    // Load context for Lightning Messaging Service 
    @wire(MessageContext) messageContext;

    //Publish ProductSelected message
    handleProductSelected(event) {  
        publish(this.messageContext, PRODUCT_SELECTED_MESSAGE, {
            productId: event.detail
        });
    }

    // Load the list of available products.
    @wire(getProducts, { searchKey: '$searchKey' })
    products;

    handleSearchKeyChange(event) {
        this.searchKey = event.target.value.toLowerCase();
    }

}

Notice in the code above that each productTile uses an onselected event to fire off the handleProductSelected function. Also notice that at the top of the JavaScript controller, publish and MessageContext functions are imported from lightning/messageService. These are required when working with Lightning Message Service (LMS).

Working with Lightning Message Service

Lightning Message Service (LMS) was introduced in Summer 20 and it offers a standard way to communicate across the DOM, or Document Object Model. LMS can be used with LWC’s, along with Aura components and even Visualforce pages – as long as they are contained within the same lightning page.

The Lightning App Page that is used to host the solution has two regions. displayProducts will be loaded into the left-hand region and on the right will be a new component named productCard.

When a user clicks a product tile in displayProducts, a message will be sent to the productCard component, allowing it to display additional detail information. Communicating with unrelated components allows a user to easily switch between the tiles and additional product information without having to navigate to an actual Salesforce detail page.

Steps to work with LMS:

  1. Create a message channel.
  2. Define scope of message channel.
  3. Publish to the message channel.
  4. Component that should receive data will subscribe to the message channel

Below is the code for the productCard component (which is the unrelated component in this example):

<!-- productCard.html -->

<template>
    <lightning-card icon-name="standard:apex_plugin">
        <template if:true={recordId}>
            <span slot="title">{productName}</span>
            <lightning-button-icon
                icon-name="utility:expand_alt"
                slot="actions"
                onclick={handleNavigateToRecord}
            ></lightning-button-icon>
            <div class="slds-var-m-horizontal_medium">
                <img
                    if:true={productPictureUrl}
                    src={productPictureUrl}
                    class="product"
                    alt="Product picture"
                />
                <lightning-record-view-form
                    record-id={recordId}
                    onload={handleRecordLoaded}
                    object-api-name="Product2"
                    density="compact"
                >
                    <lightning-output-field
                        field-name={familyField}
                    ></lightning-output-field>
                    <lightning-output-field
                        field-name={productCodeField}
                    ></lightning-output-field>
                    <lightning-output-field
                        field-name={msrpField}
                    ></lightning-output-field>
                    <lightning-output-field
                        field-name={descriptionField}
                    ></lightning-output-field>
                </lightning-record-view-form>
            </div>
        </template>
        <template if:false={recordId}>
            <div class="slds-var-p-around_large">
                <p class="placeholder">Select a product to see details</p>
            </div>
            
        </template>
    </lightning-card>
</template>

//  productCard.js

import { LightningElement, wire } from 'lwc';

// Lightning Message Service and a message channel
import { NavigationMixin } from 'lightning/navigation';
import { subscribe, MessageContext } from 'lightning/messageService';
import PRODUCT_SELECTED_MESSAGE from '@salesforce/messageChannel/ProductSelected__c';

// Utils to extract field values
import { getFieldValue } from 'lightning/uiRecordApi';

// Product__c Schema
import PRODUCT_OBJECT from '@salesforce/schema/Product2';
import NAME_FIELD from '@salesforce/schema/Product2.Name';
import PRODUCT_CODE_FIELD from '@salesforce/schema/Product2.ProductCode';
import FAMILY_FIELD from '@salesforce/schema/Product2.Family';
import MSRP_FIELD from '@salesforce/schema/Product2.MSRP__c';
import DESCRIPTION_FIELD from '@salesforce/schema/Product2.Description';
import PICTURE_URL_FIELD from '@salesforce/schema/Product2.Picture_URL__c';

/**
 * Component to display details of a Product__c.
 */
export default class ProductCard extends NavigationMixin(LightningElement) {
    // Exposing fields to make them available in template
    familyField = FAMILY_FIELD;
    msrpField = MSRP_FIELD;
    productCodeField = PRODUCT_CODE_FIELD;
    descriptionField = DESCRIPTION_FIELD;

    // Id of Product__c to display
    recordId;

    // Product fields displayed with specific format
    productName;
    productPictureUrl;

    /** Load context for Lightning Messaging Service */
    @wire(MessageContext) messageContext;

    /** Subscription for ProductSelected Lightning message */
    productSelectionSubscription;

    connectedCallback() {
        // Subscribe to ProductSelected message
        this.productSelectionSubscription = subscribe(
            this.messageContext,
            PRODUCT_SELECTED_MESSAGE,
            (message) => this.handleProductSelected(message.productId)
        );
    }

    handleRecordLoaded(event) {
        const { records } = event.detail;
        const recordData = records[this.recordId];
        this.productName = getFieldValue(recordData, NAME_FIELD);
        this.productPictureUrl = getFieldValue(recordData, PICTURE_URL_FIELD);
    }

    /**
     * Handler for when a product is selected. When `this.recordId` changes, the
     * lightning-record-view-form component will detect the change and provision new data.
     */
    handleProductSelected(productId) {
        this.recordId = productId;
    }

    handleNavigateToRecord() {
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: this.recordId,
                objectApiName: PRODUCT_OBJECT.objectApiName,
                actionName: 'view'
            }
        });
    }
}

Notice in the HTML code above (which is bolded), there is a lightning button icon component. If clicked the user will be directed to the products record page. This is just in case that is what the user would prefer to happen. Displaying the record detail information will be done using the lightning record view form base component, along with several output field base components.

Also notice in the Javascript code, import statements to access functions from the lightning message service, as well as the newly created message channel. The next import statement will include the uiRecordApi utility that allows me to extract field values for product fields, along with several import statements used to access schema metadata for specific Product2 fields.

Inside the class, I will begin by adding some local variables that will expose some of that schema metadata. Below that I need to define the scope of the message context, which in this case will just be the default of the active area. This is only available to me when using the @wire context.

Next is the handleRecordLoaded handler which is fired when the lightning record view form base component first loads. It will get the product record ID passed in the event message channel and use the uiRecordApi to extract the field values for the product name and picture URL.

The end result of this solution are two unrelated components that communicate seamlessly. The image below shows you what it looks like to have the components work together in a Lightning page for Experience Cloud.

Final rendered solution in Salesforce Experience Cloud
Final rendered solution in Experience Cloud

The code and explanations in this post is just a sample of all that is covered in the Pluralsight course, Communicate Between Salesforce Lightning Web Components”. Check that out if you are interested in learning more about how LWC’s communicate. You can also check out a free 10 minute preview video that explains how I got to this point in the post series.

Studying for the Salesforce Certified Sharing and Visibility Designer Exam?

The second course in a new Skill Path series on Pluralsight, designed to help prepare you for the Salesforce Certified Sharing and Visibility exam was just published. The new course is titled, “Share Salesforce Data Across Users, Groups, and Roles“.

Using a real-world scenario involving a global company, you’ll learn how to build a record sharing model for your Salesforce org. First, you’ll explore the Salesforce sharing architecture and the different layers of record sharing security Salesforce provides.

From there, you’ll discover how to set org-wide defaults and design a role hierarchy. Finally you will learn how to open up access when needed with sharing rules or manual sharing. When you’re finished, you will have the skills and knowledge to design a robust and secure sharing model for your Salesforce organization.

And look out for future posts on this blog about this very complex and critical piece of the Salesforce Security architecture.

Post 2 – Communicate Between Salesforce Lightning Web Components: Parent to Child

This is the latest series of posts I am doing on a new Pluralsight course I created titled, “Communicate Between Salesforce Lightning Web Components”.

Working with Public Properties

Public properties can be used to pass data from a parent component to one or more children components. For Lightning Web Components (LWC), data is passed from the parent to the child using the @api decorator.

Parent to Child Communication
Parent to Child Communication

Decorators are used in JavaScript to wrap one piece of code with another. That is why they are called decorators. The @ symbol is placed before the name of the decorator and for LWC the @api decorator is a special decorator used to expose public properties or methods.

When used with a property, these properties/fields are reactive. This means whenever the value of the property changes, the component is automatically rerendered. For this reason, each property can be associated with only one decorator. This also applies when a method is decorated.

Displaying Products

In this post, I will be working with nested components used to search for and display products. The parent or container component will be named displayProducts. The code for this component is seen below:

<!-- displayProduct.html -->

<template>
    <div class="slds-card slds-var-p-around_x-small">
        <lightning-input
            label="Search Key"
            type="text"
            onchange={handleSearchKeyChange}
            class="search-bar"
        ></lightning-input>
        <template if:true={products.data}>
          <template if:true={products.data.length}>
             <div class="content">
                <template for:each={products.data}
                     for:item="product">
                     <!-- Product Tile Component here-->
                     <c-product-tile
                        key={product.Id}
                        product={product}
                        onselected={handleProductSelected}
                        class="slds-var-m-around_x-small">
                     </c-product-tile>
                 </template>
              </div>
            </template>
            <template if:false={products.data.length}>
                <p>No products matching your selection</p>
            </template>
        </template>
        <template if:true={products.error}>
            <p>Error when getting product data</p>
        </template>
    </div>
</template>


// displayProducts.js

import { LightningElement, wire } from 'lwc';

import { publish, MessageContext } from 'lightning/messageService';
import PRODUCT_SELECTED_MESSAGE from '@salesforce/messageChannel/ProductSelected__c';

// getProducts() method in ProductController Apex class
import getProducts from '@salesforce/apex/ProductController.getProducts';

export default class DisplayProducts extends LightningElement {

    searchKey = '';     
    
    // Load context for Lightning Messaging Service 
    @wire(MessageContext) messageContext;

    //Publish ProductSelected message
    handleProductSelected(event) {  
        publish(this.messageContext, PRODUCT_SELECTED_MESSAGE, {
            productId: event.detail
        });
    }

    // Load the list of available products.
    @wire(getProducts, { searchKey: '$searchKey' })
    products;

    handleSearchKeyChange(event) {
        this.searchKey = event.target.value.toLowerCase();
    }

}

Things to notice in the bolded portion of the displayProducts.html file above:

  1. Since a search may return multiple products, a child component named productTile will be used to display just one product as a tile.
  2. An iterator will be used to display as many instances of this child component as there are records returned.
  3. I am passing the product id through the key attribute and the actual product data through the product attribute.

Here is the HTML and JS for the productTile component.

<!-- producTile.html -->

<template>
  <a onclick={handleClick}>
    <div class="content">
      <img
         src={pictureUrl}
         class="product slds-align_absolute-center"
         alt="Product picture"
      />
      <div>
         <p class="title slds-align_absolute-center">{name}</p>
         <p class="slds-align_absolute-center">
            MSRP:&nbsp;
            <lightning-formatted-number
               format-style="currency"
               currency-code="USD"
               value={msrp}
               class="price"
               maximum-fraction-digits="0">
            </lightning-formatted-number>
         </p>
      </div>
    </div>
  </a>
</template>


// productTile.js

import { LightningElement, api } from 'lwc';

export default class ProductTile extends LightningElement {

    _product;
    pictureUrl;
    name;
    msrp;


    @api
    get product() {
        return this._product;
    }
    set product(value) {
        this._product = value;
        this.pictureUrl = value.Picture_URL__c;
        this.name = value.Name;
        this.msrp = value.MSRP__c;
    }

    handleClick() {
        const selectedEvent = new CustomEvent('selected', {
            detail: this.product.Id
        });
        this.dispatchEvent(selectedEvent);
    }


}

Things to notice in the bolded portion of the productTile.js file above:

  1. api was added to the list of modules to be imported.
  2. Below that will be private variables used for the product data passed from the parent component.
  3. Data will be passed to the child through getter and setter functions, which need to be annotated with the @api decorator, in order to make them public.

The end result of all of this is a component that looks like this:

Final rendered version of the displayProducts component with child productTile component
Final rendered version of the displayProducts component with child productTile component

The code and explanations in this post is just a sample of all that is covered in the Pluralsight course, Communicate Between Salesforce Lightning Web Components”. Check that out if you are interested in learning more about how LWC’s communicate.

Building a Complex LWC app? You may need to learn more about how they communicate

Are you a Salesforce developer that has been tasked with building a complex app involving multiple Lightning Web Components (LWC’s)?

The most important thing to understand is all the ways these components can communicate. Without a solid understanding of this concept, you may struggle and inadvertently create a poor performing or error prone app.

That is why I am happy to announce that my latest Pluralsight course, “Communicate Between Salesforce Lightning Web Components” has just been released. This is not a course for beginners new to LWC’s or Salesforce. But, if you plan on building an app that involves multiple LWC’s and you already know the basics, then this course is for you.

Communicate Between Salesforce Lightning web Components

In this course you will learn how to effectively communicate between LWC’s. First you will discover the three methods used to communicate between components. Then you will learn details about each method by seeing how they work in a real-world app. By the end of the course, you will know how to build your own high-performing Lightning-based app that is assembled with Lightning Web Components. This course is focused solely on LWC communication.

I hope you find the course useful. If you do check it out and have feedback, I would love to hear it – good or bad. You can give feedback through Pluralsight or here on my blog. Either way, it would be appreciated. Thank you.

Top 5 Tips for Building Your First LWC

I am very honored to have been asked by 100daysoftrailhead.com to record a session about Top 5 Tips for Building Your 1st LWC. And with that, I wanted to write this post to summarize the tips that I featured in that presentation.

Tip # 1 – Hit the Trail

If you are brand new to Lightning Web Components, then you definitely want to begin by working through the Build Lightning Web Components trail on Trailhead. The trail several projects and modules and you do not have to work them all at once. But at the very least, you should complete the first one, Quick Start: Lighting Web Components.

Tip # 2 – Install the Salesforce Extended Expansion Pack

Better Option is to install the Salesforce Extended Expansion Pack
Better Option is to install the Salesforce Extended Expansion Pack

The official Salesforce docs tell you to install the Salesforce Extension Pack for Visual Studio Code. But, I suggest you also/or instead install the Salesforce Extended Expansion Pack, which you can do by Clicking the Extensions icon in the left toolbar of Visual Studio Code and then selecting Salesforce Extension Pack (Extended).

Select the Extended Expansion Pack though the Extensions icon in Visual Studio Code
Select the Extended Expansion Pack though the Extensions icon in Visual Studio Code

This extension pack will include 4 additional JavaScript libraries that you will more than likely need. They are XML, ESLint, Prettier and Apex PMD.

Tip # 3 – Embrace the Command Line Interface (CLI) Help

Don’t fear the CLI – Embrace it! I know that the Salesforce Extensions offers a very nifty Command Palette tool, but that does not cover everything that the CLI offers. By using the built-in help features, you not only get access to the latest docs (and that you can be rest assured of), but you can learn a lot about what the CLI Offers.

To access the help feature, just type the following sfdx help from a Terminal Window in Visual Studio Code. This will bring up results such as the following:

Access the CLI help feature from Terminal window
Access the CLI help feature from Terminal window

To drill down into one of the topics, such as the force one, use the following:

sfdx force --help

From there you can drill down as far as you need to, such as this command for accessing info about creating a Salesforce DX project:

sfdx force:project:create --help

Tip # 4 – Use Base Lightning Components Whenever Possible

The Base Lightning Components that Salesforce offers not only make your life as a developer so much easier, they are highly performant than anything you might try to create yourself. So, you should check them all out and make sure to use them whenever possible.

The Component library offers a handy Lightning Mini Playground that you can use to access sample HTML and JavaScript directly.

The Component Library offers a handy Lightning Mini Playground feature

Tip # 5 – Reference the Code in the Sample App Gallery

The Sample App Gallery includes real-world code that were all designed by the incredible developers with the Salesforce developer relations group. They not only demonstrate new Salesforce features, but best practices for how to create Lightning web components.

As you begin the process, the most important one to checkout is the LWC Recipes one. This GitHub repo features very short code snippets that demonstrate how to perform certain key operations.

Bonus Tip – Check out My New Pluralsight Course

This month, I also released my latest Pluralsight course, “Salesforce Lightning Web Components: The Big Picture“. You can find out more about it in this post. Once you have completed that course, you might want to checkout, “Building Your First Lightning Web Component (LWC) for Salesforce“.

New Pluralsight Course: Salesforce Lightning Web Components: The Big Picture

I am beyond happy to announce the release of my latest Pluralsight course, “Salesforce Lightning Web Components: The Big Picture“. This is a high-level overview of all the important things that make up this new modern and standards-based framework.

Viewers will first explore what makes up the entire Lightning Web Stack. This will include discovering the open-source Lightning Design System, which is key to the entire Lightning Experience.

Lightning Web Stack
Lightning Web Stack

Learners will also learn about the modern developer tools that Salesforce now offers. These tools offer developers a way to build robust and high-performing web apps. The tools should be instantly familiar to developers familiar with building modern web apps using frameworks like React or Vue.

Visual Studio Code and the Salesforce Extension Pack
Visual Studio Code and the Salesforce Extension Pack

When learners are finished, they should have the skills and knowledge of Lightning Web Components needed to build an adoption plan for their own Salesforce organizations.

And if you watch the course, please feel free to give me feedback. Good or bad. Thanks!

EDIT: Go here to access a promotional video I created for this course. It is a condensed 10 minute preview of the course that should give you a good idea of what it offers.