Tag Archives: Lightning Component

Invoke Flow From Lightning Component With Input Variables

Using lightning:flow component, we can invoke a flow in Lightning runtime. This component requires API version 41.0 and later. Here is an example to delete all Contact records from an Account using a flow.

Here I’ve created a flow and the flow is invoked by a lightning component, and that lightning component is configured with a QuickAction on Account object.

1. Go to Setup | Quick Find – Search Flows | New Flow

2. Create a variable as “AccountId”

3. Create a sObject Collection Variable as “CollectionOfContacts”

4. Create a Fast Lookup to get Contacts with Account Id

5. Create a Decision to check Account has contacts.

6. Default Outcome in Decision

7. Create Fast Delete to delete Contacts

8. Set the Fast Lookup as a starting element, indicated by the green down arrow. Then, connect the flow element “Fast Lookup” to “Decision” and “Decision” to “Fast Delete”.

9. Create a Lightning Component to invoke the flow.

Lightning component:

<aura:component implements="force:lightningQuickActionWithoutHeader,flexipage:availableForRecordHome,force:hasRecordId">
    
    <!--Custom Styles for Modal Header and Footer--> 
    <aura:html tag="style">
        .cuf-content {
        padding: 0 0rem !important;
        }
        .slds-p-around--medium {
        padding: 0rem !important;
        }       
        .slds-modal__content{
        overflow-y:hidden !important;
        height:unset !important;
        max-height:unset !important;
        }
    </aura:html>
    
    <!--Declare Attributes--> 
    <aura:attribute name="hasError" type="Boolean" default="false"/>
    
    <!--Modal Header-->  
    <div class="modal-header slds-modal__header slds-size_1-of-1">
        <h4 class="title slds-text-heading--medium">Delete Contacts</h4>
    </div>
    <!--End Modal Header-->  
    
    <!--Modal Body-->   
    <div class="slds-modal__content slds-p-around--x-small slds-align_absolute-center slds-size_1-of-1 slds-is-relative">
        <form class="slds-form--stacked">
            <aura:if isTrue="{!!v.hasError}">
                <div class="slds-align_absolute-center">
                    Do you want to delete all contacts?
                </div>
                <aura:set attribute="else">
                    <div>
                        <div class="slds-text-color_error">Error on Contact record deletion.
                            Please forward following error message to your admin:</div>
                    </div>
                </aura:set>
            </aura:if>
            <div>
                <!--Lightning Flow Attribute-->
                <lightning:flow aura:id="deleteContactFlow" onstatuschange="{!c.statusChange}"/>
            </div>
        </form> 
    </div>
    <!--End of Modal Body--> 
    
    <!--Modal Footer-->
    <div class="modal-footer slds-modal__footer slds-size_1-of-1">
        <lightning:button variant="Brand" class="slds-button" label="Confirm" onclick="{!c.handleConfirm}"/>
        <lightning:button variant="Neutral" class="slds-button" label="Cancel" onclick="{!c.handleClose}"/>
    </div>
    <!--End of Modal Footer-->
</aura:component>

Lightning JS Controller:

({
    //Confirm 
    handleConfirm : function(component, event, helper) {
        //Find lightning flow from component
        var flow = component.find("deleteContactFlow");
        //Put input variable values
        var inputVariables = [
            {
                name : "AccountId",
                type : "String",
                value : component.get("v.recordId")
            }
        ];
        //Reference flow's Unique Name
        flow.startFlow("Delete_Contact_From_Account", inputVariables);
    },
    
    //Close the quick action
    handleClose : function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    },
    
    //Flow Status Change
    statusChange : function (component, event, helper) {
        //Check Flow Status
        if (event.getParam('status') === "FINISHED_SCREEN" || event.getParam('status') === "FINISHED") {
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
                title: "Success!",
                message: "Contacts deleted successfully!",
                type: "success"
            });
            toastEvent.fire();
            $A.get("e.force:closeQuickAction").fire();
            $A.get('e.force:refreshView').fire();
        } else if (event.getParam('status') === "ERROR") {
            component.set("v.hasError", true);
        }
    }
})

10. Configure above created lightning component on Account object Quick Action.

11. Add created quick action on Account object page layout.

12. Here is the quick action to delete all contacts.

Get Picklist Values Dynamically In Lightning Radio Group Component

In below example I’m loading Account object Industry picklist field values in Lightning Radio Group.

Apex Controller:

public class SampleController {
    
    @AuraEnabled //Save Account Data
    Public static void createAccount(Account objacc){
        try{
            //Insert Account Record
            insert objacc; 
            
        }catch(Exception e){
            //throw exception message
            throw new AuraHandledException(e.getMessage());
        }
        finally {
        }
    }
    
    @AuraEnabled //get Account Industry Picklist Values
    public static Map<String, String> getIndustry(){
        Map<String, String> options = new Map<String, String>();
        //get Account Industry Field Describe
        Schema.DescribeFieldResult fieldResult = Account.Industry.getDescribe();
        //get Account Industry Picklist Values
        List<Schema.PicklistEntry> pList = fieldResult.getPicklistValues();
        for (Schema.PicklistEntry p: pList) {
            //Put Picklist Value & Label in Map
            options.put(p.getValue(), p.getLabel());
        }
        return options;
    }
}

Lightning Component:

<!--Sample.cmp-->
<aura:component controller="SampleController" implements="flexipage:availableForAllPageTypes,force:appHostable">
    
    <!--Declare Attributes-->
    <aura:attribute name="industryMap" type="Map"/>
    <aura:attribute name="acc" type="Account" default="{'sobjectType':'Account', 
                                                       'Name': '',
                                                       'AccountNumber': '',
                                                       'Email': '',
                                                       'Phone': '', 
                                                       'Industry': ''}"/>
    
    <!--Declare Handler-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>  
    
    <!--Component Start-->
    <div class="slds-m-around--xx-large">
        <div class="container-fluid">
            <div class="form-group">
                <lightning:input name="accName" type="text" required="true" maxlength="50" label="Account Name" value="{!v.acc.Name}" />
            </div>
            <div class="form-group">
                <lightning:input name="accNumber" type="text" required="true" maxlength="10" label="Account Number" value="{!v.acc.AccountNumber}" />
            </div>
            <div class="form-group">
                <lightning:input name="accEmail" type="email" required="true" maxlength="100" label="Email" value="{!v.acc.Email}" />
            </div>
            <div class="form-group">
                <lightning:input name="accPhone" type="phone" required="true" maxlength="10" label="Phone" value="{!v.acc.Phone}" />
            </div>
            <div class="form-group">
                <!--Lightning radio group component-->
                <lightning:radioGroup name="radioGroup"
                                      label="Industry"
                                      required="true"
                                      options="{!v.industryMap}"
                                      value="{!v.acc.Industry}"
                                      type="radio"/>
            </div>
        </div>
        <br/>
        <lightning:button variant="brand" label="Submit" onclick="{!c.handleAccountSave}" />              
    </div>
    <!--Component End-->
</aura:component>

Lightning Component Controller:

({
    //Load Account Industry Picklist
    doInit: function(component, event, helper) {        
        helper.getIndustryPicklist(component, event);
    },
    
    //handle Account Save
    handleAccountSave : function(component, event, helper) {
        helper.saveAccount(component, event);
    },
    
    //handle Industry Picklist Selection
    handleCompanyOnChange : function(component, event, helper) {
        var indutry = component.get("v.acc.Industry");
        alert(indutry);
    }
})

Lightning Component Helper:

({
    //get Industry Picklist Value
    getIndustryPicklist: function(component, event) {
        var action = component.get("c.getIndustry");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                var industryMap = [];
                for(var key in result){
                    industryMap.push({label: result[key], value: key});
                }
                component.set("v.industryMap", industryMap);
            }
        });
        $A.enqueueAction(action);
    },
    
    //handle Account Save
    saveAccount : function(component, event) {
        var acc = component.get("v.acc");
        var action = component.get("c.createAccount");
        action.setParams({
            objacc : acc
        });
        action.setCallback(this,function(response){
            var state = response.getState();
            if(state === "SUCCESS"){
                alert('Record is Created Successfully');
            } else if(state === "ERROR"){
                var errors = action.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        alert(errors[0].message);
                    }
                }
            }else if (status === "INCOMPLETE") {
                alert('No response from server or client is offline.');
            }
        });       
        $A.enqueueAction(action);
    }
})

Output:

Record Type Selector Custom Lightning Component

Apex Controller:

public class SampleAuraController {
    
    @AuraEnabled
    public string defaultRecordTypeId {get; set;}
    @AuraEnabled
    public Map<Id, String> contactRecordTypes {get; set;}
    
    @AuraEnabled        
    public static SampleAuraController getRecordTypeValues(){
        SampleAuraController obj = new SampleAuraController();
        Map<Id, String> recordtypeMap = new Map<Id, String>();
        //Get all record types of Contact object
        List<Schema.RecordTypeInfo> recordTypeInfoList = Contact.SObjectType.getDescribe().getRecordTypeInfos();
        for(RecordTypeInfo info: recordTypeInfoList) {
            //Check record type is available for current user profile
            if(info.isAvailable()) {
                //Check master record type
                if(info.getName() != 'Master' && info.getName().trim() != ''){
                    recordtypeMap.put(info.getRecordTypeId(), info.getName());
                }
                //Get the default record type for current user profile
                if(info.isDefaultRecordTypeMapping()){
                    obj.defaultRecordTypeId = info.getRecordTypeId();
                }
            }
        }    
        obj.contactRecordTypes = recordtypeMap;
        return obj;
    }
}

Lightning Component:

<aura:component controller="SampleAuraController" implements="flexipage:availableForAllPageTypes,force:appHostable">
    
    <!--Declare Attributes-->
    <aura:attribute name="recordTypeMap" type="Map"/>
    <aura:attribute name="selectedRecordTypeId" type="String"/>
    <!--Declare Handler-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>  
    
    <!--Component Start-->
    <div class="slds-m-around--xx-large">
        <div class="container-fluid">
            <lightning:radioGroup name="radioGroup"
                                  label="Select Record Type"
                                  required="true"
                                  options="{!v.recordTypeMap}"
                                  value="{!v.selectedRecordTypeId}"
                                  type="radio"/>
        </div>
        <br/>
        <lightning:button variant="brand" label="Submit" onclick="{!c.handleCreateRecord}" />  
    </div>
</aura:component>

JS Controller:

({
    doInit: function(component, event, helper) {        
        var action = component.get("c.getRecordTypeValues");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                var recordTypes = result.contactRecordTypes;
                var recordtypeMap = [];
                for(var key in recordTypes){
                    recordtypeMap.push({label: recordTypes[key], value: key});
                }
                component.set("v.recordTypeMap", recordtypeMap);
                component.set("v.selectedRecordTypeId", result.defaultRecordTypeId);
            }
        });
        $A.enqueueAction(action);
    },
    
    handleCreateRecord: function(component, event, helper) { 
        var selectedRecordTypeId = component.get("v.selectedRecordTypeId");
        if(selectedRecordTypeId){
            var createRecordEvent = $A.get("e.force:createRecord");
            createRecordEvent.setParams({
                "entityApiName": 'Contact',
                "recordTypeId": selectedRecordTypeId,
            });
            createRecordEvent.fire();
        }
    }
})

Output:

Salesforce Lightning Custom Path for Picklist Field

Now you can create your custom lightning path for picklist field of any standard and custom objects. In Winter’18 release, Salesforce introduced lightning:picklistPath component, it displays the progress of a process, which is based on the picklist field specified by the picklistFieldApiName attribute. The path is rendered as a horizontal bar with one chevron for each picklist item. Paths created by this component do not have key fields or guidance and do not display the Mark Complete button.

Here is an example to create a path for Application Custom Object records that’s based on the record Id and the Status__c picklist field.

Component:

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
    <lightning:notificationsLibrary aura:id="notifLib"/>
    <!--Picklist Field Attribute-->
    <aura:attribute name="picklistField" type="object"/>
    
    <!--Force Record Data to update picklist value-->
    <force:recordData aura:id="record"
                      layoutType="FULL"
                      recordId="{!v.recordId}"
                      targetFields="{!v.picklistField}"
                      mode="EDIT"/>
    
    <!--For changing from left-to-right use dir="ltr" and from to right-to-left use dir="rtl"-->
    <div dir="ltr">
        <!--Lightning Picklist Path For Applicatin Status-->
        <lightning:picklistPath recordId="{!v.recordId}"
                                variant="linear"
                                picklistFieldApiName="Status__c"
                                onselect="{!c.handleSelect}" />
    </div>
</aura:component>

Component JS Controller:

({
    handleSelect : function (component, event, helper) {
        //get selected Status value
        var selectStatus = event.getParam("detail").value;
        //set selected Status value
        component.set("v.picklistField.Status__c", selectStatus);
        
        component.find("record").saveRecord($A.getCallback(function(response) {
            if (response.state === "SUCCESS") {
                $A.get('e.force:refreshView').fire();
                component.find('notifLib').showToast({
                    "variant": "success",
                    "message": "Record was updated sucessfully",
                    "mode" : "sticky"
                });
            } else {
                component.find('notifLib').showToast({
                    "variant": "error",
                    "message": "There was a problem updating the record.",
                    "mode" : "sticky"
                });
            }
        }));
    }
})

Output:

Handle Trigger and Validation Rule Error in Lightning Component

Sometimes we can have a requirement to display validation rule error or apex trigger error messages, on save of record in Lightning Component. We can handle those errors from apex controller and can throw messages to Lightning Component. In lightning response object though the state value comes as ERROR but the message in the error object always says ‘Internal Server Error”. Here is an example to handle the Trigger and Validation Rule Error in apex controller to show error messages in Lightning Component.

In below example I’ve used Lead object FirstName, LastName, Email & Company Field in lightning component to create record. And I’ve created a validation rule and a trigger validation on Lead object.

1. Validation rule for Company field (Company cannot be Test Company).

2. In below Trigger I’m checking if FirstName field value is “Test” then it will throw error.

trigger LeadTrigger on Lead (before insert, before update) {
    
    for(Lead obj :Trigger.new){
        if(obj.FirstName == 'Test'){
            obj.FirstName.addError('First name cannot be test');
        }
    }
}

Apex Controller:

public class SampleAuraController {
    
    @AuraEnabled
    Public static void createLead(Lead objLead){
        String msg = '';
        try{
            //Insert Lead Record
            insert objLead; 
            
        }catch(DmlException e){
            //Any type of Validation Rule error message, Required field missing error message, Trigger error message etc..
            //we can get from DmlException
            
            //Get All DML Messages
            for (Integer i = 0; i < e.getNumDml(); i++) {
                //Get Validation Rule & Trigger Error Messages
                msg =+ e.getDmlMessage(i) +  '\n' ;
            }
            //throw DML exception message
            throw new AuraHandledException(msg);
            
        }catch(Exception e){
            //throw all other exception message
            throw new AuraHandledException(e.getMessage());
        }
        finally {
        }
    } 
}

Lightning Component:

<!--Sample.cmp--> 
<aura:component controller="SampleAuraController" implements="flexipage:availableForAllPageTypes,force:appHostable">
    
    <!--Declare Attributes-->
    <aura:attribute name="isSpinner" type="boolean" default="false"/>
    <aura:attribute name="objLead" type="Lead" default="{'sobjectType':'Lead', 
                                                        'FirstName': '',
                                                        'LastName': '',
                                                        'Email': '', 
                                                        'Company': ''}"/>
    
    
    <!--Component Start--> 
    <div class="slds-m-around--xx-large">
        <div class="container-fluid">
            <div class="form-group">
                <lightning:input name="fname" type="text" maxlength="50" required="true" label="First Name" value="{!v.objLead.FirstName}" />
            </div>
            <div class="form-group">
                <lightning:input name="lname" type="text" maxlength="50" required="true" label="Last Name" value="{!v.objLead.LastName}" />
            </div>
            <div class="form-group">
                <lightning:input name="emailId" type="email" maxlength="100" required="true" label="Email" value="{!v.objLead.Email}" />
            </div>
            <div class="form-group">
                <lightning:input name="company" type="text" maxlength="50" required="true" label="Company" value="{!v.objLead.Company}" />
            </div>
        </div>
        <br/>
        <lightning:button variant="brand" label="Submit" onclick="{!c.handleLeadSave}" />        
        <lightning:button label="Cancel" onclick="{!c.handleCancel}"/>        
    </div>
    <!--Component End-->
</aura:component>

Lightning JS Controller:

({    
    //Handle Lead Save
    handleLeadSave : function(component, event, helper) {
        var objLead = component.get("v.objLead");
        var action = component.get("c.createLead");
        action.setParams({
            objLead : objLead
        });
        action.setCallback(this,function(a){
            var state = a.getState();
            if(state === "SUCCESS"){
                alert('Record is Created Successfully');
            } else if(state === "ERROR"){
                var errors = action.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        alert(errors[0].message);
                    }
                }
            }else if (status === "INCOMPLETE") {
                alert('No response from server or client is offline.');
            }
        });       
        $A.enqueueAction(action);
    }
})

Output:

Trigger Error Message:

Validation Rule Error Message: