+ Start a Discussion
Greg FinzerGreg Finzer 

How to handle validation errors in a lightning add/edit modal popup?

I have created an Add/Edit modal popup that is working well.  I have a couple required validations and also a couple custom validation rules.  How do I get the validation errors and display them?

Here is my component:
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" controller="FRCAddRenoPopupController">
    <aura:attribute name="accountId" type="Id"/>
	<aura:attribute name="PageHeading" type="String" default="Add Renovation" />
    
    <aura:attribute name="reno" type="FRC_Renovation__c" default="{'sobjectType' : 'FRC_Renovation__c',
                                                                  'Id' : '',
                                                                  'Name' : '',
                                                                  'Property__c' : '',
                                                                  'Renovation_Start_Date__c' : '',
                                                                  'Renovation_End_Date__c' : '',
                                                                  'Renovation_Notes__c' : '',
                                                                  'Scope_Confirmed__c' : false }"/>    
       
    <lightning:button label="Add Renovation"
                      iconName="utility:new_window"
                      iconPosition="left"
                      variant="brand"
                      onclick="{!c.newPopup}"
                      />   
    
    <aura:method name="editPopupMethod" action="{!c.editPopup}" access="public">
      <aura:attribute name="columnIdentifier" type="String" />
    </aura:method>
    
    <div role="dialog" tabindex="-1" aria-labelledby="header43" aura:id="renoAddModal" class="slds-modal slds-modal_large">
        <div class="slds-modal__container" style="width: 65%;">
            <div class="slds-modal__header" style="font-size: 1.5em">
                {!v.PageHeading}             
            </div>
            
            <div class="slds-modal__content slds-p-around--medium">
                <div class="slds-p-left_xx-large slds-p-right_xx-large" style="padding-left:0px">
                    <div class="slds-page-header" style="padding-top: 9px; padding-bottom: 9px;padding-right: 9px;">
                        <h3 style="font-size: 1rem;" title="">Renovation Information</h3>
                    </div> 
                </div>    
                <div class="slds-grid slds-p-top_medium">
                    <div class="slds-size_11-of-12 slds-p-left_xx-large slds-p-horizontal_x-large " >
                        <lightning:input label="Renovation Name" required="true" name="renovationName" value="{!v.reno.Name}"/> 
                    </div>
                </div>
                <div class="slds-grid slds-p-top_x-small">
                    <div class="slds-size_6-of-12 slds-p-left_xx-large slds-p-horizontal_x-large " >
                        <ui:inputDate label="Renovation Start Date" aura:id="renovationStartDate" required="true" value="{!v.reno.Renovation_Start_Date__c}" displayDatePicker="true" format="MM/dd/yyyy"/>
                    </div>
                    <div class="slds-size_5-of-12 slds-p-left_xx-small slds-p-horizontal_x-large " >
                        <ui:inputDate label="Renovation End Date" aura:id="renovationEndDate" value="{!v.reno.Renovation_End_Date__c}" displayDatePicker="true" format="MM/dd/yyyy"/>                        
                    </div>
                </div>
                <div aura:id="scopeConfirmedDiv" class="slds-grid slds-p-top_medium">
                    <div class="slds-size_11-of-12 slds-p-left_xx-large slds-p-horizontal_x-large " >
                        <lightning:input label="Scope Confirmed" type="checkbox" name="scopeConfirmed" checked="{!v.reno.Scope_Confirmed__c}"/> 
                    </div>
                </div>                 
                <div class="slds-grid slds-p-top_medium">
                    <div class="slds-size_11-of-12 slds-p-left_xx-large slds-p-horizontal_x-large " >
                        Renovation Notes<br />
                        <lightning:inputRichText label="Renovation Notes" value="{!v.reno.Renovation_Notes__c}" />
                    </div>
                </div>                
            </div>
            <div class="slds-modal__footer">
                <lightning:button label="Save" onclick="{!c.saveModal}" />
                <lightning:button label="Cancel" onclick="{!c.closeNewModal}" />
            </div>
        </div>
    </div>   
    <div aura:id="Modalbackdrop" class="slds-backdrop"></div>
</aura:component>

Here is my controller:
({
    editPopup : function(component, event, helper)
    {
        var args = event.getParam("arguments");
        var columnIdentifier = args.columnIdentifier;
                
        var action = component.get("c.loadReno");
        action.setParams({renoID  : columnIdentifier});
        
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
				component.set("v.PageHeading", "Edit Renovation");
                var record = response.getReturnValue();
                component.set("v.reno.Id", record.Id);
                component.set("v.reno.Name", record.Name);
                component.set("v.reno.Property__c", record.Property__c);
                component.set("v.reno.Renovation_Start_Date__c", record.Renovation_Start_Date__c);
                component.set("v.reno.Renovation_End_Date__c", record.Renovation_End_Date__c);
                component.set("v.reno.Renovation_Notes__c", record.Renovation_Notes__c);
                component.set("v.reno.Scope_Confirmed__c", record.Scope_Confirmed__c);

                helper.applyShowFormStyles(component);
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + 
                                    errors[0].message);
                    }
                } 
                else {
                    console.log(response.getReturnValue());
                }
            }
        });
        $A.enqueueAction(action);
        
      
    },
    newPopup : function(component, event, helper)
    {
        //Initialize Fields
        component.set("v.PageHeading", "Add Renovation");
        component.set("v.reno.Id", "");
        component.set("v.reno.Name", "");
        component.set("v.reno.Property__c", "");
        component.set("v.reno.Renovation_Start_Date__c", "");
        component.set("v.reno.Renovation_End_Date__c", "");
        component.set("v.reno.Renovation_Notes__c", "");
        component.set("v.reno.Scope_Confirmed__c", false);
        
        helper.applyShowFormStyles(component);
        
        //Hide the scope confirmed checkbox when adding a renovation
        var cmpScopeConfirmed = component.find('scopeConfirmedDiv');
        $A.util.addClass(cmpScopeConfirmed, 'slds-hide');
    },
	closeNewModal : function(component, event, helper)
    {
        helper.applyHideFormStyles(component);
    },
    saveModal : function(component, event, helper){
        var renoAttr = component.get("v.reno");        
        var accountIdAttr = component.get("v.accountId");
        console.log(renoAttr.Id);
        
        if (renoAttr.Id == '')
        {
            helper.insertReno(component, helper, renoAttr, accountIdAttr);
        }
        else
        {
            helper.updateReno(component, helper, renoAttr);
        }		
    },
})

Here is my helper:
({
	applyHideFormStyles : function(component) {
		var cmpTarget = component.find('renoAddModal');
        $A.util.removeClass(cmpTarget, 'slds-fade-in-open');

        var cmpBack = component.find('Modalbackdrop');
        $A.util.removeClass(cmpBack,'slds-backdrop_open');        
                        
        var cmpScopeConfirmed = component.find('scopeConfirmedDiv');
        $A.util.removeClass(cmpScopeConfirmed, 'slds-hide');
	},
	applyShowFormStyles : function(component) {
        var cmpTarget = component.find('renoAddModal');
        $A.util.addClass(cmpTarget, 'slds-fade-in-open');

        var cmpBack = component.find('Modalbackdrop');
        $A.util.addClass(cmpBack, 'slds-backdrop_open');
	},
	insertReno : function(component, helper, renoAttr, accountIdAttr) {
        var action = component.get("c.insertReno");
        action.setParams({reno  : renoAttr, accountId  : accountIdAttr});
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                helper.applyHideFormStyles(component);               
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + 
                                    errors[0].message);
                    }
                } 
                else {
                    console.log(response.getReturnValue());
                }
            }
        });
        $A.enqueueAction(action);
	},
	updateReno : function(component, helper, renoAttr) {
        var action = component.get("c.updateReno");
        action.setParams({reno  : renoAttr});
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                helper.applyHideFormStyles(component);               
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + 
                                    errors[0].message);
                    }
                } 
                else {
                    console.log(response.getReturnValue());
                }
            }
        });
        $A.enqueueAction(action);
	}      
})



 
Best Answer chosen by Greg Finzer
Maharajan CMaharajan C
Hi Greg,

If you want to get the Validation Error in Lightning while your inserting or Updating the FRC_Renovation__c record then you have to fire the Aura Exception from your Apex Class Like below :

Public static lead insertReno(FRC_Renovation__c reno ,Id accountId)
    {              
        String errormsg =''
        try{
            insert     reno;
        }
        catch(DmlException ex){
            for(Integer i=0; i < ex.getNumDml(); i++){
                errormsg =+ ex.getDmlMessage(i) + '\n';
            }
                throw new AuraHandledException(errormsg);     
            }

        catch(Exception e){
            throw new AuraHandledException(e.getMessage());
        }
        finally{
        }        
    }

In JS Controller :  (This will show the errors in Toast Msg)

insertReno : function(component, helper, renoAttr, accountIdAttr) {
        var action = component.get("c.insertReno");
        action.setParams({reno  : renoAttr, accountId  : accountIdAttr});
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                helper.applyHideFormStyles(component);               
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            var toastEvent = $A.get("e.force:showToast");
                            toastEvent.setParams({
                                "title": "Error!",
                                "message": errors[0].message,
                                'type': 'error'
                            });
                            toastEvent.fire();
                            console.log("Error message: " + 
                                        errors[0].message);
                        }

                    } else {
                        console.log("Unknown error");
                    }
            }
        });
        $A.enqueueAction(action);

    }

if you want to show the Error messages inside the Component instead of Toast then Add one More Attribute to store the Error Message and create the new div element or area in top of the component to display the error message and then use the Aura:if to control this error div only display if the arror attribute is not empty.


Can you please Let me know if it helps or not!!!

If it helps don't forget to mark this as a best answer!!!


Thanks,
Maharajan.C

All Answers

Maharajan CMaharajan C
Hi Greg,

If you want to get the Validation Error in Lightning while your inserting or Updating the FRC_Renovation__c record then you have to fire the Aura Exception from your Apex Class Like below :

Public static lead insertReno(FRC_Renovation__c reno ,Id accountId)
    {              
        String errormsg =''
        try{
            insert     reno;
        }
        catch(DmlException ex){
            for(Integer i=0; i < ex.getNumDml(); i++){
                errormsg =+ ex.getDmlMessage(i) + '\n';
            }
                throw new AuraHandledException(errormsg);     
            }

        catch(Exception e){
            throw new AuraHandledException(e.getMessage());
        }
        finally{
        }        
    }

In JS Controller :  (This will show the errors in Toast Msg)

insertReno : function(component, helper, renoAttr, accountIdAttr) {
        var action = component.get("c.insertReno");
        action.setParams({reno  : renoAttr, accountId  : accountIdAttr});
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                helper.applyHideFormStyles(component);               
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            var toastEvent = $A.get("e.force:showToast");
                            toastEvent.setParams({
                                "title": "Error!",
                                "message": errors[0].message,
                                'type': 'error'
                            });
                            toastEvent.fire();
                            console.log("Error message: " + 
                                        errors[0].message);
                        }

                    } else {
                        console.log("Unknown error");
                    }
            }
        });
        $A.enqueueAction(action);

    }

if you want to show the Error messages inside the Component instead of Toast then Add one More Attribute to store the Error Message and create the new div element or area in top of the component to display the error message and then use the Aura:if to control this error div only display if the arror attribute is not empty.


Can you please Let me know if it helps or not!!!

If it helps don't forget to mark this as a best answer!!!


Thanks,
Maharajan.C
This was selected as the best answer
Greg FinzerGreg Finzer
Maharajan,

Thanks for your help.  I ended up doing something similar:

Controller:
@AuraEnabled
    public static void updateReno(FRC_Renovation__c reno) 
    {
        if (reno.Name == '')
            throw new AuraHandledException('Required fields are missing: Renovation Name');

        FRC_Renovation__c renoToSave = loadReno(reno.Id);

        renoToSave.Name = reno.Name;
        renoToSave.Renovation_Start_Date__c = reno.Renovation_Start_Date__c;
        renoToSave.Renovation_End_Date__c = reno.Renovation_End_Date__c;
        renoToSave.Scope_Confirmed__c = reno.Scope_Confirmed__c;
        renoToSave.Renovation_Notes__c = reno.Renovation_Notes__c;

        try {
           update renoToSave;
        } catch (Exception ex) {
           throw new AuraHandledException(FRCHelper.GetDmlMessages(ex));
        }      
    }

FRCHelper class:
public static string GetDmlMessages(Exception ex)
    {
        string result = '';        

        for (Integer i = 0; i < ex.getNumDml(); i++) 
        {
            result += ex.getDmlMessage(i);

            if (i < ex.getNumDml() -1)
                result += '<br />';
        }

        result = result.replace('[', '');
        result = result.replace(']', '');

        return result;
    }

My Helper Class in the Lightning Component:
updateReno : function(component, helper, renoAttr) {
        var action = component.get("c.updateReno");
        action.setParams({reno  : renoAttr});
        action.setCallback(this, function(response) {
            var state = response.getState();          
            if (state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                helper.applyHideFormStyles(component);               
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + 
                                    errors[0].message);
                    }
                    
                    component.set("v.ErrorMessages", errors[0].message);
                    var errorMessagesSection = component.find('errorMessagesSection');
        			$A.util.removeClass(errorMessagesSection, 'slds-hide');                     
                } 
                else {
                    console.log(response.getReturnValue());
                }
            }
        });
        $A.enqueueAction(action);
	}

I just put in an error messages div referring to an attribute instead of the toast:
<div aura:id="errorMessagesSection" class="slds-hide errorStyle">
	{!v.ErrorMessages} 
</div>