Blog

The Basics of Calling a Lightning Component from a Salesforce Lightning Detail Page

The Basics of Calling a Lightning Component from a Salesforce Lightning Detail Page

Terry Luschen  |  December 20, 2017

A few years ago, I wrote this post: The Basics of Calling a Visualforce Page from a Salesforce Detail Page. It illustrated a very simple way to use a custom button from a detail page to call a Visualforce Page with an Apex Controller, so some action on that record could be taken, and then return to the detail page.

I cannot tell you how many times I have re-used that pattern in the past five years.

Also, it is known that if you leverage this old Visualforce pattern within the Lightning user interface, the detail page will not refresh. I think many orgs will need to be refactored over to the new Lightning Component pattern below to keep an experience that does not force the user to refresh the page after the action.

Now it is time to update the pattern. Lightning is here to stay, and we need to embrace it to do the same thing.

The 6 Ingredients You Need

What ingredients do we need to execute code within an Apex Class from a detail page in the Lightning Experience?

  1. 1 Apex Class
  2. 1 Test Class
  3. 1 Lightning Component
  4. 1 Action
  5. Adding the action to a page layout
  6. And a little love never hurts!

Once we mix them properly, we’ll have the foundation to accomplish anything that Apex and Lightning Components can provide. Think of it like creating a great pie crust. This is your foundation, and then you can add any pie filling you want. (Anybody for Strawberry-Rhubarb?)

Let’s get started …

1. Apex Class

  • This is our Apex Controller. You can do anything you want Apex to do in here.
  • Notice that basically the ID for any object could be passed into here. You just need to build in a bit more logic to choose what fields you’d be updating on different objects.
  • In the Visualforce world in the past, we needed different Visualforce Pages for each object type for the StandardController, so we could tie to a custom button. Now we can have a fully generic Lightning Component and Apex Controller.
public class MyController {

 

   
@AuraEnabled

   public
static string updateObject(Id objectID) {

       
// Perform isAccessible() and isUpdateable() checks here

       if(string.isBlank(objectID)){

//This is the standard way to throw exceptions back up to the Javascript Controller/Helper

           throw new AuraHandledException('There was no ID passed to be saved.');

       }

 

       try{           

           Schema
.SObjectType sObjType = objectID.getSObjectType();

           string objectAPIName = sObjType.getDescribe().getName();

          //Logic could be introduced here to update specific fields for specific objects

           string fromClauseFields = ‘ID, AccountNumber’;

           Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objectAPIName);

           Schema.DescribeSObjectResult dsor = targetType.getDescribe();

           List<String> fieldList = new List<String>();

           fieldList = fromClauseFields.split(', ');

          //This is a great spot to check your field level security

           if(!FieldSecurity.fieldCheck(dsor, fieldList)){

               throw
new System.NoAccessException();           

           }

           list
<sObject> sObjList = database.Query('Select ID, ' + fromClauseFields +

                                            ' From ' + objectAPIName +

                                            ' Where ID = :objectID');

           if(sObjList.size() > 0){

               sObjList[0]
.AccountNumber = ‘123’;

               Update sObjList[0];

           }

       }catch
(exception e){

           throw
new AuraHandledException('Error saving object.  Details: ' + e.getMessage());

       }

       return
'';

   }



<span>}</span>

<
span>

2. 1 Test Class

  • I won’t write that for you. (Ha.)
  • It will depend on your logic.
  • Remember your Asserts.
  • Remember to write small methods.

3. 1 Lightning Component

  • Notice the force:lightningQuickActionWithHeader, which will allow us to select this Lightning Component when we create our Action.
  • The force:hasRecordID will automatically pass the Salesforce ID to use, and we can use the v.recordId syntax to grab that ID.
  • When this runs, the Lightning Component will come up while the action takes place. (If somebody can tell me a way to not show any window at all, I would love to know!)
  • This line is the key to getting the Salesforce record detail page to refresh in the Lightning user interface: $A.get(“e.force:refreshView”).fire();
<aura:component controller="MyController" implements="force:lightningQuickActionWithoutHeader,force:hasRecordId">

   <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

   <div class="slds-page-header" role="banner">

       <p class="slds-text-heading--label">Action in Progress</p>

       <h1 class="slds-page-header__title slds-m-right--small slds-truncate slds-align-left">Please Wait...</h1>

   </div>

 

</aura:component>

 

 

({

   doInit
: function(component, event, helper) {

       
//Setup the call to the Apex Controller and also pass one parameter

       var action = component.get("c.updateObject");

       action.setParams({"objectID": component.get("v.recordId")});

 

       
// Configure response handler

       action.setCallback(this, function(response) {

           var state
= response.getState();

           // Prepare a toast UI message

           var resultsToast = $A.get("e.force:showToast");

           if(state === "SUCCESS") {                               

               resultsToast
.setParams({

                   
"title": "Update - Success",

                   "message": "The object was updated.."

               });               

           }
else if (state == "ERROR"){

               var errors
= response.getError();

               if (errors) {

                   if
(errors[0] && errors[0].message) {

                       resultsToast
.setParams({

                           
"title": "Update Error",

                           "message": "The update validation returned an error: " + errors[0].message

                       }
);

                   }

               }
else {

                   resultsToast
.setParams({

                       
"title": "Update Unknown Error",

                       "message": "The update returned an error: " + state

                   }
);

               }

           }
else {

               resultsToast
.setParams({

                   
"title": "Update Unknown Error",

                   "message": "The update returned an error: " + state

               }
);

           }

           resultsToast
.fire();

           //This is the key to getting the page to refresh

           $A.get("e.force:refreshView").fire();

 

           
//This closes the Action Window

           var dismissActionPanel = $A.get("e.force:closeQuickAction");

           dismissActionPanel.fire();

       });

 

       
//This calls the Apex Controller and the code will restart on the setCallback line.

       $A.enqueueAction(action);       

   }

 

}
)

4. 1 Action

Now you can simply create an Action on the Account object, and point the Action at this new Lightning Component.

5. Adding the action to page layout

Then just add your action to the Account page layout.

6. And again, a little love never hurts!

When you are ready to make the transition from Visualforce to Lightning, I hope this pattern helps you get started quickly. Once the basics of this recipe are in place, you’ll be back to creating great functionality every day. Time to dig in! And if you have any questions, reach out to our team anytime.

Ask Our Experts

Posted in: Lightning, Lightning Bolt, Salesforce, Web Development

Share