Blog

Lightning Web Components (LWC) from Quick Actions

Written by Trieste LaPorte | Nov 12, 2021 4:33:34 AM

Introduction

If you’re reading this, chances are you have some legacy custom buttons that include Javascript. Chances are those buttons do some really cool things that so far have been difficult to replicate, especially javascript buttons that invoke Apex web services.


It has been possible to replace these things with Aura (Lightning) Components, for a while. But if you’re like me, you probably never really liked working with Aura and living with tech debt was far more attractive. We’ll be covering how to do these things in LWC, or Lightning Web Components. These components are far friendlier to write and maintain.

Salesforce now (Summer ‘21) allows Lightning Web Components to be called by Quick Actions, which means they can replace those old Javascript buttons without a huge change to the users.

Furthermore, Javascript buttons only work in Salesforce Classic.

I’m not going to go into the many ways that reliance on Javascript Buttons can be avoided, or the functionality can be implemented in other ways such as components directly on FlexiPages, since that sort of thing has a higher potential of causing grief with users.

I also want to make a callout to Tushar Sharma who wrote this article before I got around to it, and they provide great instructions and samples. I decided I still wanted to write an article from a different perspective - and include some reasons why this whole effort is worth it. I’ll also include some other patterns involved that were not covered earlier. Also, not included in the original article are the nuances around the lifecycle of the page, and why connectedCallback is not reliable.

All the code in this document is available here: https://github.com/Foglight-Solutions/quickActionLWC

The Classic Scenario - Javascript Buttons

The Javascript Button let us do some really cool stuff. If you are reading this for fun and never got to experience them - they allowed arbitrary Javascript code to build payloads and send them to Apex web service methods, which then did some work, and returned a response payload to the Javascript that could then be acted on further. This was incredibly powerful and is a big reason why a lot of Admins prefer to stay on the Classic experience. One way we can fully replicate the functionality of Javascript buttons is by using a Quick Action that calls a Lightning Web Component. I’ll start with examples.

Read This

  • getRecordNotifyChange is new, and is not the subject of this article, but it came out about the same time as the ability to wire LWCs to Quick Actions. If you don’t make a call to this method then the parent record page will show old data even after the button is clicked.
  • I provided no click protection. It’s possible for a user to click the Quick Action as many times as they like, and that will fire off the headless LWC that many times, even if it’s not done processing.

Apex Controller

  1. Method - This is the name of our method, and this simple example expects only a single parameter representing an Account record Id.
  2. Method Body - Nothing exciting here, we’re just newing up an account and updating it. If the update fails, ‘return true’ will never be reached and an unhandled exception will surface to the LWC Javascript.

LWC Meta

  1. This is just the API version.
  2. Is Exposed - This lets this component be included in things that are not other LWCs - front ends, flexiPages, buttons.
  3. Where can this component be included? - This is what allows the component to be targeted by a QuickAction.
  4. When included as a RecordAction, what type of action is it? - This is what says that it’s an immediate action, or headless action. There is no UI, it will just do work.

LWC JavaScript

  1. Imports - Here we want our method from the controller
  2. recordId - This is automatically populated by the calling quick action, and contains the record id of the current record seen by the user.
  3. async invoke - The Quick Action is hardwired to call the Invoke function
  4. Apex method call - Here we’re making the call to the Apex method, and doing whatever else we might want to do. We can include toast or show alerts, or make additional method calls here. We’re also calling getRecordNotifyChange provided by the platform in order to refresh the record in the standard UI.

LWC HTML

This doesn’t need to have anything special in it. It does not render to the user.

Quick Action Configuration

  1. Lightning Web Component Selection - Here we select our Lightning Web Component. It shows up here because of the target configuration in the LWC’s Meta file.
  2. Name and Label - Use what you like here, this is how it shows up to the user.

Layout Configuration

  1. Edit the Layout, and select Mobile & Lightning Actions
  2. Drag the new Quick Action to the Lightning Experience Actions section - You might have to override the standard actions before you’re able to add custom actions here.

Headless Conclusion

We’ve just described how we can wire up javascript and apex to a button that exists on the Lightning Record Page. Using these basics as a foundation, you should be able to replicate any of the legacy Javascript buttons you had before. I recommend reading Tushar’s article linked earlier as a method for preventing multiple clicks is described in detail, and is a good idea.

LWC Modals from Quick Actions

This one is new for LWCs, kinda new for Aura, and was impossible before Lightning without doing a lot more work.

What we’re talking about here is the ability to pop a modal with custom markup from a record page. The options with VisualForce were overriding the page entirely, or dragging a VisualForce page component into the standard layout, or popping a new browser window with the content inside. We could not simply pop a modal in the same browser window based on a button that lived in the standard buttons section.

Aura added the ability to do this, but like we covered before, Aura is just not very friendly to work in. Being able to do this easily using Lightning Web Components is huge. Again, we’ll just show some examples.

Read This

There are some workarounds included in the code shown throughout. I’ll point it out along the way, but keep an eye out for:

  • We are using renderedCallback instead of connectedCallback. The connectedCallback does not get the recordId in time to do work. In here we’re also using some flags to prevent constructing the variables more than once.
    • In order for renderedCallback to function we have to include something in the page HTML that renders. For this example I used the recordId, and I hid it in a div with display set to none.
    • If you don’t set the page up this way you’re going to have a tough time getting the recordId from the calling page. By tough, I mean I’ve never figured it out.
  • getRecordNotifyChange is new, and is not the subject of this article, but it came out about the same time as the ability to wire LWCs to Quick Actions. If you don’t make a call to this method then the parent record page will show old data when the modal closes. This text looks familiar if you read the previous section regarding headless actions.
  • I did not use @wire to get the Account record in order to demonstrate how to perform complex page construction. For a simple page, using @wire would be more efficient.
  • I did not include any sort of form processing or validation. It works just fine, in fact, anything you can do in LWC regarding forms would work fine here too.

Apex Controller

  1. Setup Method Definition
  2. Setup Method Body - Earlier I mentioned this could have all been done by wiring up a record (using @wire). I wanted to show calling a method to get the page setup information. This pattern supports complex payloads as well as just a single record.
  3. Save Method Definition
  4. Save Method Body - Nothing special here. This method could take an Account record built by the front end Javascript and save it, or perform validation, or do whatever you can think of. This one is simply using Math.random to show that the update did in fact happen.

LWC Meta

  1. This is just the API version (same as before).
  2. Is Exposed - This lets this component be included in things that are not other LWCs - front ends, flexiPages, buttons. (again, same)
  3. Where can this component be included? - This is what allows the component to be targeted by a QuickAction. (and the same!)
  4. When included as a RecordAction, what type of action is it? - This is what’s different from the last meta file. Here we’re telling the system that this LWC has something to render, and will show a screen to the user.

LWC Javascript

  1. @api recordId; - This is our standard @api enabled recordId variable. Salesforce will push the recordId into this variable when the page is called from a quick action. The value will be the id of the record the user is viewing.
  2. tracked variables
    1. completedLoading - We’re using this as a recursion protection mechanism. renderedCallback will be called multiple times throughout the page’s lifecycle, and we only want to do the setup work once.
    2. accountRecord - This is just an example of including a record in a page.
  3. connectedCallback - You would normally use connectedCallback to do your initialization work in a LWC. Here I’ve included a debug statement that helps to illustrate how, in this scenario, connectedCallback is the wrong place to do the work. recordId is not populated when connectedCallback is called by the system.
  4. renderedCallback - Here’s where we do our page setup instead. But since renderedCallback is called several times, we have some checks to make sure it does work once. All the method is doing here is retrieving an Account record, but this could be where complex page setup is performed.
  5. renderedCallback recursion logic - Once we do work, we want to set the appropriate flags.
  6. onSaveClick - Simple example of a save method. Note that we’re not doing any sort of form validation or retrieving of form values.
  7. getRecordNotifyChange and modal closure - Here we’re telling the system, outside this modal, that we’ve updated the record. This will refresh any values that changed in the user’s UI.
  8. closeThisModal - Close the modal if we’re successful.

LWC HTML

  1. Force renderedCallback to fire. - renderedCallback only fires when something is rendered that the controller is bound to. Without this reference, the renderedCallback is never called, since all other variables are in the conditionally rendered block below, which has not been rendered yet. The order of events in this scenario are (first two order is irrelevant):
    1. renderedCallback fires (recordId is null)
    2. connectedCallback fires (recordId is null)
    3. Platform sets recordId, and renderedCallback is fired a second time, but this time it can see a non-null value for recordId.
      1. This method gets the Account, sets the completedLoading variable to true, and the contents render.
  2. Only render stuff with references to the Account if the Account has actually been retrieved. - This is 1.c.i above, LWC Javascript will throw a fit if we tried to render accountRecord.Name and accountRecord was left null. Another option is to instantiate accountRecord as an object ({}) and populate it later.
  3. Save button - Just a simple call to our save method for the demo.

Quick Action Configuration

  1. Lightning Web Component Selection - Here we select our Lightning Web Component. It shows up here because of the target configuration in the LWC’s Meta file. (same as before, but we’re selecting the modal component)
  2. Name and Label - Use what you like here, this is how it shows up to the user.

Layout Configuration

  1. Edit the Layout, and select Mobile & Lightning Actions
  2. Drag the new Quick Action to the Lightning Experience Actions section - You might have to override the standard actions before you’re able to add custom actions here.

This one is super cool. We’ve not been able to do something like this without a lot of hackery before. The amount of work is not much to get it going. I wanted to focus on the pieces that were not super obvious, like using renderedCallback and the actions defined in the meta file.