Error Handling in Lightning components
Been playing with few approaches for handling errors properly and propagate the Server errors onto my lightning components.
What Happens When an Exception Occurs?
From Server side :
When an exception occurs, code execution halts. Any DML operations that were processed before the exception are rolled back and aren’t committed to the database. Exceptions get logged in debug logs. For unhandled exceptions, that is, exceptions that the code doesn’t catch, Salesforce sends an email that includes the exception information. The end user sees an error message in the Salesforce user interface.
Common Exceptions
There is a full list of Apex Exceptions (Link) – those are the common ones in lightning components:
- DmlException – Any problem with a DML statement, such as an insert statement missing a required field on a record.
- ListException – Any problem with a list, such as attempting to access an index that is out of bounds.
- NullPointerException – Any problem with dereferencing null.
- SObjectException – Any problem with sObject records, such as attempting to change a field in an update statement that can only be changed during insert.
- QueryException – Any problem with SOQL queries, such as assigning a query that returns no records or more than one record to a singleton sObject variable.
- JSONException – Any problem with JSON serialization and deserialization operations. For more information, see the methods of System.JSON, System.JSONParser, and System.JSONGenerator.
How to avoid :
Typically you should anticipate in your code which type of exceptions might occur and handle it accordingly.
- Wrap the code that can trigger exceptions in a try-catch block.
- Throw an
AuraHandledException
in the catch block. This allows you to provide a custom user-friendly error message.
For any type of operation you anticipate that might fail – use this approach.
try, catch syntax
// Best practice: user-friendly error message provided by an AuraHandledException @AuraEnabled public static void triggerBasicAuraHandledError() { try { integer a = 1 / 0; // Division by zero causes exception } catch (Exception e) { // "Convert" the exception into an AuraHandledException throw new AuraHandledException('Darn it! Something went wrong: ' + e.getMessage()); } finally { // Something executed whether there was an error or not } }
Example of a generic approach that use the generic Exception :
Create an Error Class
public class ErrorMSG { @AuraEnabled public Object exceptionType {get;set;} @AuraEnabled public Object message {get;set;} @AuraEnabled public Object cause {get;set;} @AuraEnabled public Object lineNumber {get;set;} @AuraEnabled public Object stackTrace {get;set;} @AuraEnabled public String query {get;set;} @AuraEnabled public String customMSG {get;set;} public ErrorMSG (String customMSG, String query, Object exceptionType,Object message,Object cause,Object lineNumber,Object stackTrace) { this.customMSG = 'Something went wrong ' + customMSG; this.exceptionType = exceptionType; this.message = message; this.cause = cause; this.lineNumber = lineNumber; this.stackTrace = stackTrace; this.query = query; } }
Catch and Throw the Exception
try { List<Sobject> objectList = Database.query(queryString); } catch (QueryException e) { ErrorMSG error = new ErrorMSG ('Running Query',queryContextRecords,e.getTypeName(), e.getMessage(),e.getCause(),e.getLineNumber(),e.getStackTraceString()); // "Convert" the exception into an AuraHandledException throw new AuraHandledException( JSON.serialize(error, true) ); }
This statement return the errorMap details into the client side handler where we can grab the errors and display to User.
Handle Errors on Client Side
callServer : function(component) { var errors = ''; var message = 'Unknown error'; // Default error message var action=component.get("c.serverMethod"); // set param to method action.setParams({ 'contextId': component.get('v.recordId') }); //Store in cache action.setStorable(); // set a callBack action.setCallback(this, function(response) { var state = response.getState(); if (state === "SUCCESS") { var data = JSON.parse(response.getReturnValue()); component.set('v.items',data.results); message = state + ' - in loading the data..'; } else if (state === "ERROR") { errors = response.getError(); message = state + ' - In Server Method' ; this.throwError(component, message, errors[0].message); \ } else if (status === "INCOMPLETE") { message = 'No response from server or client is offline.'; this.showToast (component,'INFO', message, errors); } else { // Handle other response states errors = response.getError(); message = state + ' - In Server Method'; this.throwError(component, message, errors[0].message); } }); $A.enqueueAction(action); } },
Display with Toast notification Success or Errors :
showToast: function(component, type, message, errors) { // Configure toast let toastParams = { title: "Success", message: message type: type }; // Pass the error message if any if (errors && Array.isArray(errors) && errors.length > 0) { toastParams.message = errors[0].message; } // Fire toast let toastEvent = $A.get("e.force:showToast"); toastEvent.setParams(toastParams); toastEvent.fire(); }
Show Full error message on screen using a ui:message created component :
throwError: function(component, message, details) { $A.createComponents([ ["ui:message",{ "title" : message, "severity" : "error", "closable":"true" }], ["ui:outputText",{ "value" : details }]], function(components, status, errorMessage){ if (status === "SUCCESS") { var message = components[0]; var outputText = components[1]; // set the body of the ui:message to be the ui:outputText message.set("v.body", outputText); var errorMsgdiv = component.find("errorMsg"); // Replace div body with the dynamic component errorMsgdiv.set("v.body", message); } else if (status === "INCOMPLETE") { console.log("No response from server or client is offline.") // Show offline error } else if (status === "ERROR") { console.log("Error: " + errorMessage); // Show error message } } );
On the component level if using display ui:message :
<lightning:layout verticalAlign="center" class=""> <lightning:layoutItem class="" size="12"> <!-- Error Messages --> <div aura:id="errorMsg"/> </lightning:layoutItem> </lightning:layout>