Salesforce Development Tutorial (LWC): How to use Lightning Web Component’s in List View Buttons

Why Bother Using an LWC in a List View Button?

If you need to create some custom functionality that is accessible to your users via a list view (or potentially a related list), then a list view button is the way to go. The reason that you should prefer to utilize an LWC (despite the fact there is no obvious way to use an LWC for a list view button) is because LWC’s load faster than any other component type and, of the available list view button options, they are the most consistent with Salesforce’s Lightning Experience look and feel.

Unfortunately, while it’s super simple to setup and LWC for a list view button, Salesforce has no documentation on how to do so and virtually no answers exist for how to do this anywhere online, but NO MORE!! Today I’ll show you three different methods of creating an LWC List View button! Two methods do not allow you to send the ids of selected records in a list view and one does. I’ll give you the pros and cons of each and how to setup each one below.

One additional note, all of the below solutions will allow the user to traverse from the list view button back to the exact list view they were previously on without the help of visualforce at all! Something else that was undocumented and challenging to figure out.

DISCLAIMER: As of this writing, LWC quick actions are not usable on list views, this could change in the future however, so make sure to investigate that.


Setting up the Lightning Web Component

In any of the three scenarios we are going to use the same Lightning Web Component to demo the functionality, although in the third scenario (a flow based scenario) we will be making slight modifications to allow for the ids of records to get passed through to the component. Let’s take a look at the core of the component below (or on GitHub here).

HTML File:

<template>
	<lightning-button label="Return to List View" onclick={close}></lightning-button>
</template>

JS File:

import {LightningElement} from 'lwc';

export default class ListViewButton extends LightningElement {

	close(){
		setTimeout(
			function() {
				window.history.back();
			},
			1000
		);
	}
}

XML File:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <description>List View Button</description>
    <isExposed>true</isExposed>
    <masterLabel>List View Button</masterLabel>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__Tab</target>
        <target>lightning__FlowScreen</target>
    </targets>
</LightningComponentBundle>

Alright alright alright, so we have all of the files outlined above, let’s make talk about the close function in the JavaScript file first. I did an ENORMOUS amount of experimentation trying to get a list view button that spawns an LWC to allow me to traverse back to the exact list view that I had previously come from and this was the only way I could consistently get that to happen. I tried navigationmixin and it had trouble remembering where to go no matter what I did, I tried return urls, I tried a ton of junk, but that close function above does the trick every single time (at least in a browser, never tried on a mobile app). It always goes back to the exact list view I was on when I clicked the button.

So what does that function actually do? It basically looks to your browser windows history and moves back one page in time, the set timeout function allows it to take up to one second to make that happen. I would suggest using the set timeout as I had issues using this function without it, occasionally it wouldn’t have enough time to operate and failed. This however is used by thousands of users in my org now with no complaints.

The second thing I wanna go over is the XML File’s targets. For simplicity I just put every target we are going to need when we go through each of the three LWC list view button methods, that being said, you only need to declare the target for the method you choose to use. If you use the App Builder method, use the AppPage target, if you use the Tab method, use the Tab target, etc.


LWC List View Button Method 1: The Lightning App Builder Page Method

WHETHER YOU USE THIS METHOD OR NOT, READ THROUGH IT!! THE VAST MAJORITY OF THE STEPS ARE IDENTICAL FOR ALL OTHER VERSIONS SO THEY WILL ONLY BE SHOWN HERE AND REFERENCED IN THE OTHER SECTIONS!! Admittedly, this is my least favorite method, however it does work and some people may prefer it, so I’m gonna show you how to do it real quick. To use this method we need to create a lightning app builder app page. If you don’t know what that is, check out this trailhead. Once you’ve created the app builder page, you need to drag and drop the LWC (shown in the section above) onto the page. You can find the LWC in the section show below:

After you’ve placed your LWC on the page, save and activate your App Builder page. Then click anywhere in your lightning app page canvas that isn’t specifically your LWC to bring up the page information on the right side of the screen. Grab the “Developer Name” value, you need it for the next step.

Now that an app builder page houses our component, and we have the dev name for the app page, we need to setup a list view button to pop open our page for us. Kewlio Julio, let’s get to it.

Go to the object manager and find the object you are creating a list view button for. On the object page, click the “buttons links and actions” link, then click the “New Button or Link” on the top right of that page.

On the new button or link page, you are gonna fill out the follow:
1) Fill out a Label (this can be whatever you want)
2) Select the “List Button” display type
3) Select the “Display in existing window without sidebar or header” Behavior.
4) Select the “URL” Content Source
5) In the free text area put the following URL: /lightning/n/App_Page_Developer_Name
6) Save your button

To place your new LWC list view button on your objects list view, click on the “Search Layouts for Salesforce Classic” tab on your object and then click the drop down arrow next to the “List View” layout and select the “Edit” value in the drop down.

On the edit page for the list view layout, scroll down to the “Custom Buttons” section and select your new list view button.

Now, if you traverse to your object in the app launcher, you should be able to see your button on any list view and click it. This should result in your LWC popping up for you as shown below!

The Pros and Cons of this approach are the following:
Pros:
1) It’s the second fastest of the three options to load
Cons:
1) You cannot get rid of the app builder title area without janky css hacks
2) The tab method (outlined below) loads considerably faster.
3) This can load in an iframe depending on your settings.
4) Can’t pass in list view selection ids


LWC List View Button Method 2: The Lightning Component Tab Method

This is my absolute favorite method, it loads ultra fast and is the easiest to setup. If you don’t need list view ids passed into your LWC, this is the way to go in my opinion.

This method works much like the first method, the setup is virtually identical aside from the fact that you setup a Lightning Component Tab to use as opposed to a Lightning App Builder App Page. Even the List View button setup is the same, the only difference is that you use the lightning tabs developer name at the end of the URL. So to save my hands some typing I’m only gonna show you the tab setup, please refer to the rest of the steps in the App Builder setup instruction above.

To setup a tab to use instead of an app builder page is simple. In setup go to Tabs. Then on the Tabs screen, scroll down to the “Lightning Component Tabs” and select the “New” button.

On the new lightning component tab screen select your lightning component (the one shown above or the one you’ve built), enter a tab label, a tab name and select a tab style and you’re done. MAKE SURE YOUR LWC HAS A TARGET IN THE XML FOR TABS (this is shown in the code up above), otherwise it won’t be selectable.

Once you’ve created your tab, just follow the exact steps outlined in the app builder app page scenario to for the lightning button setup and you’re done!

Pros and Cons of this approach:
Pros:
1) Fastest load time
2) Easiest setup
3) Never loads in an iFrame
Cons:
1) Cannot load in list view ids from selected list view values


LWC List View Button Method 3: The Flow Screen Method

I will urge you to please not use this method unless you absolutely need the selected list view ids passed into your LWC. I say this because the load times are significantly slower and now you have to involve two technologies (flow and lwc) instead of one, making it more complex to deal with, albeit not by a ton.

The steps for setting up the actual list view button for this method are virtually identical to others as well, aside from the URL structure for the list view button, which we will cover, but refer to the first method for setting up the majority of the actual button.

Alrightyyyyy then, here we go. This final method utilizes a flow to allow us to capture the incoming selected list view ids and send them to our LWC to manipulate.

The first thing we need to do is update our LWC a bit to allow it to receive these incoming list view ids, so let’s do thattttttt. I’ll post the code below and then discuss it.

HTML:

<template>
	<p>These are the list view ids passed: {listViewIds}</p>
	<lightning-button label="Return to List View" onclick={close}></lightning-button>
</template>

JS:

import {LightningElement, api} from 'lwc';

export default class ListViewButton extends LightningElement {
	@api listViewIds;

	close(){
		setTimeout(
			function() {
				window.history.back();
			},
			1000
		);
	}
}

XML:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <description>List View Button</description>
    <isExposed>true</isExposed>
    <masterLabel>List View Button</masterLabel>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__Tab</target>
        <target>lightning__FlowScreen</target>
        <target>lightning__RecordAction</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__FlowScreen">
            <property name="listViewIds" type="String[]"></property>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

First let’s talk about the JavaScript file. We added two things to that file, the first is that we are now importing api at the top so that we can use the api decorator. The second is that we have created the listViewIds variable with the @api decorator on it. This allows the variable to be written to by the flow.

Next let’s talk about the metadata file, in the metadata file we have added a targetConfig. This target config allows us in the flow builder to declaratively assign the incoming list view ids to the LWC’s listViewIds variable.

Last, in the HTML file we have just created a paragraph tag to view the list view ids when they are brought over.

Now that we’ve updated the component, we need to create the flow. In setup, go to Flows and then select “New Flow” at the top to create a new flow.

You will immediately be presented with options for the type of flow you’d like to create, select “Screen Flow” and press the “Next” button., then select the “Freeform” option. You will then land on the flow builder canvas.

The first thing we need to do is create a variable. In the “Toolbox” area on the left side of the screen click the “Manager” tab and then click the “New Resource” button.

After clicking the “New Resource” button a modal will pop-up. Do the following:
1) For Resource Type select “Variable”
2) Fill out the API Name field with the value “ids” (do not include the surrounding quotes). IT MUST BE THIS VALUE TO WORK!
3) For “Data Type” select “Text”
4) Check the, “Allow Multiple Values (Collection)” checkbox
5) Check the, “Available for input” checkbox
6) Click the “Done” button


After setting up this variable you’ll need to grab a screen flow from the “Elements” tab in the toolbox and drag it onto the flow canvas.

On the Screen element modal that pops up you’ll want to do the following:
1) Enter a label and API Name
2) Uncheck the “Show Header” checkbox
3) Uncheck the “Show Footer” checkbox
4) On the left side of the modal in the “Components” area select your LWC and drop it on the page
5) Fill out the API Name for your component
6) place the “ids” variable we created above in the “listViewIds” box for the LWC
7) Click the “Done” button

Then connect your start node to your new screen node, grab the API name of your flow from the settings area of the flow canvas, activate your flow and you’re done!

The one and ONLY step that changes for the list view button setup for the flow variety is the url structure. The URL structure should be changed to the following: /flow/Flow_Developer_Name

Aside from the above, all of the other steps are the same, so please reference the first LWC Button setup method above for more info.

Pros and Cons of this method:
Pros:
1) This is the only method that can receive the ids of values selected in a list view

Cons:
1) This is by far the slowest loading method
2) It forces you to use a flow to embed your LWC
3) It’s the most complex setup
4) It hosts itself in an iFrame

Alright, that’s all folks, this blog post was long and my hands are tired. Hasta Luego!!!!!


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture

Salesforce Development Tutorial (LWC): How to use Data Attributes to easily pass data from your component HTML template to your JavaScript Controller

What are Data Attributes and Why Should I Use Them?

Data attributes are a truly magical thing that will, at some point, get you out of some prickly situations as a front end developer. They are essentially a way of storing data on an html element so that when a JS event gets fired your JS controller can easily get access to the data on the HTML element that fired the event.

This is especially useful in scenarios where you want to use for:each templates to generate tables, tabs or whatever else on the screen.


How to use Data Attributes (Example Code and Explanation)

Using data attributes is easy peasy lemonnnnnnnnnnn squezzyyyyyyyy… I’m sorry, it just felt right. Seriously though, it’s easy. Let’s first take a look at how we setup our HTML Template to use data attributes on a button element.

<template>
<lightning-button onclick={getDataAttributes} label="Click Me Bruh" data-donkey="donkeysAreKewl" data-turtle="NinjaTurtles"></lightning-button>
</template>

You see those weird attributes on my element? The “data-donkey” and “data-turtle” attributes? Those are data attributes and as you can see they can be named anything! However they must be in this format: data-randomnameyouchoose. Anything can come after the “data-” when setting up the data attributes on your element (do make sure each attribute has a unique name though!). Pretty cool right? The best part comes next though! Let’s check out the JavaScript Controller’s getDataAttributes method.

import {LightningElement} from 'lwc';

export default class LwcDataAttributes extends LightningElement {
	getDataAttributes(event){
		console.log('This is the data set ::: ' + 
                JSON.stringify(event.target.dataset));
		console.log('This is the data set turtle ::: ' + 
                JSON.stringify(event.target.dataset.turtle));
	}
}

You see that console log that has the “event.target.dataset” value in it? That event.target.dataset produces a Map that houses all of your data attributes in it. The output looks like this:

{"donkey":"donkeysAreKewl","turtle":"NinjaTurtles"}

As you can see it’s a key value pair, the key is whatever you named your data attribute on the HTML Element (notice the data- is excluded however) and the value is whatever value you assigned to that element on your HTML Element.

Now, you may also noticed in the console log below that one that we have the “event.target.dataset.turtle”, this line directly accesses the “data-turtle” value so it will just output “NinjaTurtles”. Pretty niftyyyyy! If you used event.target.dataset.donkey you would get the “donkeysAreKewl” value.

And to be honest that’s really all there is to it, there is one other quick thing we should review though.


What is an Event and the Difference between event.target and event.currentTarget

Boy oh boy does this really confuse people, so let me break it down right quick. Whatever element has the JS event attached to it is the one sending the event parameter to your JS method when you trigger the event (for instance when you use an onclick JS event on an HTML element and then click it to invoke the JS method). THIS IS IMPORTANT! I say this because, it is… trust me, but more importantly you can get into some tricky situations with event.target and event.currentTarget.

The key difference here is that event.target is the TRUE TARGET OF YOUR CLICK! and event.currentTarget is the ELEMENT THAT FIRED THE JS EVENT!

Even if you’ve never done it, you can sometimes wrap multiple elements within a div, and that div is actually the one that houses the onclick event. If you click a button within that div, the button is the “event.target” and the div is the “event.currentTarget”. Be wary of this! If you start to see null values or values you don’t expect in your dataset in your JS controller, this is more than likely why! It has confused many a person (including myself), so just make sure you are paying close attention to which target you are using in your controller.


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture

Salesforce Development Tutorial(LWC): How to Generate a Word Document from a Lightning Web Component

Why Create an LWC that can Generate Word Documents?

This is a little more self explanatory than many of the blog posts I do, but let’s go over some things. You typically wanna create this because the business has a need (for one reason or another) to generate a word doc. I’ve had businesses need them so important people could sign off on something with a hand written signature, needed guest list printed for campaigns/events and several other scenarios.

As far as why we should use an LWC to do this instead of a VF Page or Aura Component, Aura Components are considerably slower and I would just suggest not making them anymore in general and VF Pages suffer from view state limitations. While it’s easier to deal with them when working with external libraries because of lightning locker service, it’s easy to generate a document with images that blows past the 170kb view state limit and then crashes your page.


The docx.js Javascript Library

To generate word documents, we need to use the docx.js javascript library, which thankfully, is locker service compliant! Saves us a lot of time (if you didn’t know you can modify most libraries to make them compliant). You can get the docxjs code we’re gonna be using for this tutorial here .

This library basically allows you to generate word documents using javascript. It makes your life doing this a thousand times easier, so make sure to thank the devs that built it!


Writing the Code

The code we’re gonna write to get this done is just for a simple example. We’re gonna generate a list of contacts associated with an account in a word document. Before we get started, all this code is up on my GitHub here, so if you wanna just ignore this whole section and check out the GitHub repo, feel free, otherwise, please carry on, lol. So first things first, open up VSCode and create a new lightning web component! If you aren’t familiar with how to setup VSCode, I have a video covering it here!

Once you’ve got your new LWC created in VSCode, we need to upload the docxjs code to static resources so that we can use it in our LWC. You can grab the docxjs code here. Then navigate to static resources in setup and upload the docxjs code there. Make sure to make the static resource public!

After that’s done, switch back over to VSCode and let’s import the docxjs file into the LWC by using the code below:

import { LightningElement} from 'lwc';
import {loadScript} from "lightning/platformResourceLoader";
import docxImport from "@salesforce/resourceUrl/docx";

export default class Contact_list_generator extends LightningElement {

    connectedCallback(){
        Promise.all([loadScript(this, docxImport)]).then(() =>{
            //call some code here
        });
    }
}

You may be looking at the above like, “wtf is that bro?” so let me explain. The connectedCallback method is called when your LWC is loaded into the browser, so it’s kinda like the init method in Aura components. Promise.all is just saying, “Hey, I promise to wait until all the scripts are loaded, then I’m gonna execute the code inside this code block”. The loadScript is a module that Salesforce provides to you that allows you to load in resources to your LWC from static resources. Last, but certainly not least, the, “import docxImport from “@salesforce/resourceUrl/docx”;” is the actual reference to your docx static resource file. The docx at the end of the of that line should be whatever you actually named your static resource.

Next let’s add the html below to our LWC

<template>
    <div class="slds-p-bottom_x-large">
        <lightning-button class="hidden slds-float_left slds-p-right_medium" onclick={startDocumentGeneration} label="Build Document"></lightning-button>
        <a href={downloadURL} download="ContactList.docx" class="slds-hide slds-button slds-button_brand slds-float_left" >Download Document</a>         
    </div>
</template>

In the HTML above we’re basically just creating two buttons, one to generate a word document and one to download that word document. You’ll notice there are two references in the HTML to js variables/methods that don’t exist yet (startDocumentGeneration and downloadURL) so let’s get back to the LWC js controller and figure this thing out.

The next thing we need to add is a way to render the generate document button after the component loads the docxjs script. We can do that with the following code

    connectedCallback(){
        Promise.all([loadScript(this, docxImport)]).then(() =>{
            this.renderButtons();
        });
    }

    renderButtons(){
        this.template.querySelector(".hidden").classList.remove("hidden");
    }

In our connected callback method we’re gonna call a method called renderButtons that changes the visibility of our buttons after our scripts are loaded. The “this.template.querySelector(“.hidden”).classList.remove(“hidden”);” is removing the class that was hiding the component and allowing it to be viewed and clickable. We do need to actually add the css though. So let’s add the css below to the component

.hidden{
    display: none;
}

Basically that css just allows you to hide an element… pretty simple. Not much there.

The next thing we need to do is create the startDocumentGeneration method and actually grab our contact data and build the document. So let’s look at the rest of the controller code we need to build out below.

import { LightningElement, api } from 'lwc';
import {loadScript} from "lightning/platformResourceLoader";
import docxImport from "@salesforce/resourceUrl/docx";
import contactGrab from "@salesforce/apex/ContactGrabber.getAllRelatedContacts";

export default class Contact_list_generator extends LightningElement {

    @api recordId;
    downloadURL;
    _no_border = {top: {style: "none", size: 0, color: "FFFFFF"},
	bottom: {style: "none", size: 0, color: "FFFFFF"},
	left: {style: "none", size: 0, color: "FFFFFF"},
	right: {style: "none", size: 0, color: "FFFFFF"}};

    connectedCallback(){
        Promise.all([loadScript(this, docxImport)]).then(() =>{
            this.renderButtons();
        });
    }

    renderButtons(){
        //this.template.querySelector(".hidden").classList.add("not_hidden");
        this.template.querySelector(".hidden").classList.remove("hidden");
    }

    startDocumentGeneration(){
        contactGrab({'acctId': this.recordId}).then(contacts=>{
            this.buildDocument(contacts);
        });
    }

    buildDocument(contactsPassed){
        let document = new docx.Document();
        let tableCells = [];
        tableCells.push(this.generateHeaderRow());

        contactsPassed.forEach(contact => {
            tableCells.push(this.generateRow(contact));
        });

        this.generateTable(document, tableCells);
        this.generateDownloadLink(document);
    }

    generateHeaderRow(){
        let tableHeaderRow = new docx.TableRow({
            children:[
                new docx.TableCell({
                    children: [new docx.Paragraph("First Name")],
                    borders: this._no_border
                }),
                new docx.TableCell({
                    children: [new docx.Paragraph("Last Name")],
                    borders: this._no_border
                }) 
            ]
        });

        return tableHeaderRow;
    }

    generateRow(contactPassed){
        let tableRow = new docx.TableRow({
            children: [
                new docx.TableCell({
                    children: [new docx.Paragraph({children: [this.generateTextRun(contactPassed["FirstName"].toString())]})],
                    borders: this._no_border
                }),
                new docx.TableCell({
                    children: [new docx.Paragraph({children: [this.generateTextRun(contactPassed["LastName"].toString())]})],
                    borders: this._no_border
                })
            ]
        });

        return tableRow;
    }

    generateTextRun(cellString){
        let textRun = new docx.TextRun({text: cellString, bold: true, size: 48, font: "Calibri"});
        return textRun;
    }

    generateTable(documentPassed, tableCellsPassed){
        let docTable = new docx.Table({
            rows: tableCellsPassed
        });

        documentPassed.addSection({
            children: [docTable]
        });
    }

    generateDownloadLink(documentPassed){
        docx.Packer.toBase64String(documentPassed).then(textBlob =>{
            this.downloadURL = 'data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,' + textBlob;
            this.template.querySelector(".slds-hide").classList.remove("slds-hide");
        });
    }
}

So, there’s a bit to cover here, lol, so let’s start with the call in to the apex controller to get our contacts. This line of code here:

contactGrab({'acctId': this.recordId}).then(contacts=>{
            this.buildDocument(contacts);
        });

This calls to the apex controller and retrieves a list of contacts based on the account id of the record we’re currently on. If you didn’t know, the @api recordId variable declaration at the top of the class just dynamically pulls in the id of the record your component is being viewed on! Super convenient! We are also able to call our apex method using the contactGrab({‘acctId’: this.recordId}) statement because we imported our apex class at the top of the LWC here “import contactGrab from “@salesforce/apex/ContactGrabber.getAllRelatedContacts”;”. That being said we haven’t looked at the apex code yet, so let’s check it out… there’s not much there but it’s still important.

public with sharing class ContactGrabber {
    @AuraEnabled
    public static List<Contact> getAllRelatedContacts(Id acctId){
        return [SELECT Id, FirstName, LastName FROM Contact WHERE AccountId = :acctId];
    }
}

The @AuraEnabled declaration allows us to import this method in our class to the LWC. It’s import to do that, so don’t forget!

Now that we have our contacts we actually need to generate the document. So let’s get to it bruh! The buildDocument method starts this process so let’s check it our first.

buildDocument(contactsPassed){
        let document = new docx.Document();
        let tableRows = [];
        tableRows.push(this.generateHeaderRow());

        contactsPassed.forEach(contact => {
            tableRows.push(this.generateRow(contact));
        });

        this.generateTable(document, tableRows);
        this.generateDownloadLink(document);
    }

In the code above we’re declaring a new docx Document object with the “new docx.Dcoument()” declaration. After this we create an array of table cells (because in this example we are building a table of contacts in a word document). We then proceed to push a table row into the table rows array by calling the generateHeaderRow method in our js controller. Let’s check out that class next.

generateHeaderRow(){
        let tableHeaderRow = new docx.TableRow({
            children:[
                new docx.TableCell({
                    children: [new docx.Paragraph("First Name")],
                    borders: this._no_border
                }),
                new docx.TableCell({
                    children: [new docx.Paragraph("Last Name")],
                    borders: this._no_border
                }) 
            ]
        });

        return tableHeaderRow;
    }

The generateHeaderRow method use the docx.TableRow object, the docx.TableCell object and the docx.Paragraph object to generate a table row with two cells. One cell for the contacts first name and another cell for a contacts last name. It then returns this table row.

Let’s get back to the buildDocument method now. The next thing that happens is we iterate through the list of contacts that we pulled from our apex controller and generate a table row for each contact by calling the generateRow method and push that into our tableRows array. So let’s look at the generateRow method next.

generateRow(contactPassed){
        let tableRow = new docx.TableRow({
            children: [
                new docx.TableCell({
                    children: [new docx.Paragraph({children: [this.generateTextRun(contactPassed["FirstName"].toString())]})],
                    borders: this._no_border
                }),
                new docx.TableCell({
                    children: [new docx.Paragraph({children: [this.generateTextRun(contactPassed["LastName"].toString())]})],
                    borders: this._no_border
                })
            ]
        });

        return tableRow;
    }

This code does something similar to the generateHeaderRow method, the only difference between the two is that I call the generateTextRun method instead of just outright declaring a new docx Paragraph object. The docx.TextRun object allows us to specify traits in our text. Things like font size, font type, whether the text is bold and a ton more. Let’s check out the generateTextRun method to see what it’s doing.

generateTextRun(cellString){
        let textRun = new docx.TextRun({text: cellString, bold: true, size: 48, font: "Calibri"});
        return textRun;
    }

In the method above we are generating what docxjs calls a text run and then returning it. It’s pretty simple as you can see. I’m just declaring the traits I want for my text. Nothing more, nothing less.

Back to the buildDocument method then! The next thing we do is call the generateTable method and pass is our docx.Document object along with our array of tableRows. Let’s check out that method next!

generateTable(documentPassed, tableCellsPassed){
        let docTable = new docx.Table({
            rows: tableCellsPassed
        });

        documentPassed.addSection({
            children: [docTable]
        });
    }

In this method we are creating a new docx.Table and assigning the array of rows we passed to this method to the rows parameter of the docx.Table. We then proceed to add a new section to our docx.Document and put the table in that section. This actually adds the table to the document we are creating.

Now, one last time, let’s check out the buildDocument method again. The last thing we do in it is call the generateDownloadLink method. So let’s take a look at that method now.

generateDownloadLink(documentPassed){
        docx.Packer.toBase64String(documentPassed).then(textBlob =>{
            this.downloadURL = 'data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,' + textBlob;
            this.template.querySelector(".slds-hide").classList.remove("slds-hide");
        });
    }

What this method does, is take the document we built and create a url that will allow us to download the word doc. It also turns on the download button in our LWC. We generate a base64 encoded string using the docx.Packer object and assign it to the downloadURL.

And believe it or not, that’s it! Yea!!! You’ve just figured out how to build your own LWC that can produce word documents. You can build off this base to do whatever you think you might wanna do. You could, with the help of other js libraries build a whole document templating app. I’ve done it in the past, it’s challenging, but doable! Good luck building whatever it is you’re building with this!


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture

Salesforce Development Tutorial (LWC): How to Setup the LWC Local Development Server

Why setup the Local Development Server?

After you use it a couple times I promise you won’t ever ask this question again, lol. It makes your LWC development SO MUCH FASTER!!! I mean it might actually be Lightning Fast! (I’m sorry, but I’m not letting that joke go…).

If you’ve ever developed LWC’s directly in Salesforce you know that a handful of things can happen, your refresh times after updating your LWC’s can be ultra slow. Additionally, even after turning off persistent caching in your dev org, you will occassionally still have your component cached. That leaves you sitting there refreshing and wondering, “Wtf… how is this even possible? Do I really suck at code this much?” At least those are the thoughts I often have, lol.

With the Local Development Server, you don’t have to worry about those things ever again. It makes SF development so much easier to deal with.


What you need to install to use the Local Development Server

To setup the Local Development Server, you need to do the following things:

1) Install the Salesforce CLI
2) Install either VSCode or the IntelliJ IDE and the Salesforce plugins for them
3) Create an SFDX Project in either IDE listed above
4) Install the Local Development Server Salesforce CLI plugin by running the following command:

sfdx plugins:install @salesforce/lwc-dev-server

There is a VERY important thing to note! In the documentation Salesforce provides for how to setup the local development server it states you need a, “Developer Hub-enabled org” to use the local dev server. THIS IS NOT TRUE! I mean, it’s in your best interest to start using scratch orgs if you can, but enabling the dev org is not a requirement for setting up the local dev server. I’m not sure if at some point it used to be required, but it isn’t anymore.


How to run the Local Development Server

After you have done the installation steps above, you can start using your local development server in one of two ways.

1) If you are in Visual Studio code, you can bring up the command palette (ctrl+shift+p) and type in the following command: SFDX: Open Local Development Server. This command will start the development server and open a browser tab for it after it has successfully started up.

2) If you are in another IDE, you can run the following CLI command from within your SFDX project:

 sfdx force:lightning:lwc:start

This command will start your development server, but not open it in a browser tab automatically. You will then how to type the following address into a new tab in your browser: http://localhost:3333/

Then that’s it! You should be up and running with your Local Development Server!


How to change the port your Local Development Server runs on (Among other things)

If you need to alter some of your local development server configuration settings, you can do that!

You just need to create a file in the root of your SFDX project called localdevserver.config.json and add the relevant JSON to it. You can see an example here from Salesforce themselves, however the JSON example they have is actually incorrect, you have to make sure to wrap you names in double quotes (which is not shown in the Salesforce example. I’ll show you what I mean below with an example:

{
  "port": 3333
}

It’s super important to add those double quotes are the name in your JSON name value pairs. Otherwise nothing is gonna work!


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture

Salesforce Development (LWC): How to Communicate between Aura Components and Lightning Web Components Using Custom Events and the Api Decorator

Why Would You Want to Communicate Between Component Types?

There are a bunch of reasons it’s beneficial to communicate between component types but the most common ones that I have found are the following:

1) You’re building a new component and realize you need functionality that only Aura Components support, but it’s a very small piece of the component. Instead of building the entire thing in an outdated and much less performant Aura Component, you build most of it in an LWC and just communicate to a small Aura component to do the function only it can do.

2) You’re staffed in an Aura heavy org and when you start building new things for the existing Aura components you should build them in LWC. You can easily do this, but you need to know how the communication channels work between component types.

Once you see how easy it is to communicate between components it’ll be much easier to switch to LWC’s to do development for everything moving forward.


How to Communicate from an LWC to an Aura Component

This is super simple but it’s confusing if you’ve never done it before. We’re basically just gonna create a custom event in our LWC and handle that event in the Aura Component. Let’s check out the code and then I’ll explain it a bit:

<!--This is the LWC HTML-->
<template>
    <lightning-input class="dataToSend" placeholder="Enter text to transfer">
    </lightning-input>
    <lightning-button variant="brand" label="Pass data to Aura Component" 
    onclick={communicateToAura} ></lightning-button>
</template>
//This is the LWC JS Controller
import {LightningElement} from 'lwc';

export default class LwcCommunication extends LightningElement
{
    //This method creates a custom event that dispatches itself.
    //The Aura component then handles this event
    communicateToAura()
    {
         console.log('Communicating to Aura ::: ');

         //We are grabbing the value from the lightning input field that has the dataToSend 
         //class
         let dataToSend = this.template.querySelector(".dataToSend").value;

        //We are creating a custom event named senddata and passing a value in the detail 
        //portion of the custom event
        const sendDataEvent = new CustomEvent('senddata', {
            detail: {dataToSend}
        });

        //Actually dispatching the event that we created above.
        this.dispatchEvent(sendDataEvent);
    }
}
<!--This is the Aura Component HTML-->
<aura:component description="Aura_Communication">
	<aura:attribute name="dataReceived" type="String"/>

	<!--The onsenddata is what handles the custom event we made in our LWC-->
	<c:lwc_Communication onsenddata="{!c.receiveLWCData}" aura:id="lwcComp" />
        <p>This is the data receieved from our LWC: {!v.dataReceived}</p>
</aura:component>
({
       //This is the Aura JS Controller
	
        //This method receives data from our LWC and sets the dataReceived
	//Aura attribute with the events dataToSend parameter (this is the name of the 
        //variable we send in the LWC)
	receiveLWCData : function(component, event, helper)
	{
	    component.set("v.dataReceived", event.getParam("dataToSend"));
	}
});

Alright so now that you’ve checked out the code let’s go over this just a lil bit. In the communicateToAura method above you can see that we create a CustomEvent object and we give it the name ‘senddata’, we also pass some data in the custom event by using the detail property of our Javascript custom event object.

Then in the Aura component’s html we can see that we import our lightning web component using this line:

<c:lwc_Communication onsenddata="{!c.receiveLWCData}" aura:id="lwcComp" />

You can see that when we import our lightning web component that we have an onsenddata event that calls a method in the Aura Components javascript controller called receiveLWCData. When you dispatch your senddata event in your lightning web component the Aura component handles it with the onsenddata event attached to the lightning web component.

Finally you can see that we get the data from the event that was passed to us in the receieveLWCData in the Aura Components JS controller. The most important part of the method is the event.getParam(“dataToSend”). You are grabbing the variable that you passed into the detail property of your CustomEvent object in your LWC. Let me put them side by side so you see exactly what I mean:

//LWC Custom Event code
const sendDataEvent = new CustomEvent('senddata', {
            detail: {dataToSend}
});

//Aura code
component.set("v.dataReceived", event.getParam("dataToSend"));

And believe it or not it’s really that simple. You have successfully passed data from your LWC to your Aura component. Now let’s figure out how to do this in reverse.


How to Communicate from an Aura Component to an LWC

Alright so how do we do the exact opposite of what we did above?? It’s pretty simple but we need to leverage the wonderful @api decorator for lightning web components to make this work as opposed to Aura event communication. Alright so let’s check out the code below and then I’ll go over it a bit more in detail:

<!--This is the LWC Template/HTML-->
<template>
	<p>This is the data received from the Aura Component: {dataReceived}</p>
</template>
//This is the LWC JS Controller
import {LightningElement, api, track} from 'lwc';

export default class LwcCommunication extends LightningElement
{
	//Tracked variables ensure that they are refreshed on the page when their values are
	//updated in the code
	@track dataReceived;

	//The api decorator makes this a public method that any component that houses this 
        //component can access/call
	@api receiveData(data)
	{
	    this.dataReceived = data;
	}
}
<!--Aura Component HTML-->
<aura:component description="Aura_Communication">

	<!--The onsenddata is what handles the custom event we made in our LWC-->
	<c:lwc_Communication aura:id="lwcComp" />

	<lightning:input aura:id="dataToPass" />

	<lightning:button label="Pass data to LWC" onclick="{!c.passDataToLWC}"/>
</aura:component>
({
        //This is the Aura Component JS Controller

	//This method sends out data to our LWC from the Aura component.
	passDataToLWC : function(component, event, helper)
	{
		let stringToSend = component.find("dataToPass").get("v.value");

		//We are calling the receieveData method in our Lightning Web Component here
		component.find("lwcComp").receiveData(stringToSend);
	}
});

Alright, so as you can see nothing super complicated just some weird stuff you may not be super comfortable with yet. There are two very important pieces to making the passage of data between components possible.

The first is the @api method in the LWC. The receiveData method has the @api decorator in front of it. This makes the method public available to its parent component regardless of what component type it is.

The second is the component.find(“lwcComp”).receiveData(stringToSend) line in the passDataToLWC method in the Aura Components Javascript controller. This is finding the lwc that we imported into our Aura Component by its aura:id and then calling the receiveData method in the LWC (the method with the @api decorator) and passing it data.

This is surprisingly simple thankfully, no weird hacky tricks necessary just a few lines of code and we’re all good to go! If any of this is super confusing please check out the video above. I go over every aspect of the code in great detail.


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps