How to Create Modals in Lightning Component (lightning:overlayLibrary)

on

|

views

and

comments

I have come up with many situations where a Developer needs to open the Modal after clicking on the Custom Button or Link and for that Developer usually get the code the SLDS and use in the Component. So, from now developer do not need to use the code from the SLDS as From Spring 18 Salesforce has provided “overlayLibrary” for the developer to create the custom Modal (lightning:overlayLibrary).
Key points to remember about modal that are given below: –
  1. Header – The title of the modal
  2. Body – Main content of the Modal
  3. Footer – Content that relies on the bottom of the Modal. For example, Cancel, Save, Update buttons.

 

In this example, we will use 3 components and the details are given below

  1. Main Component (MainComponent.cmp) – Will contain lightning:overlayLibrary tag with aura:id attribute and a lightning:button
  2. Modal Content (ModalContent.cmp) – The component responsible for containing all the content to show in the Modal as content
  3. Modal Footer(ModalFooter.cmp) – The component will contain 2 buttons one for closing the Modal and other for performing the action as per requirement.

 

Step1- Create modalContent.cmp – File -> New -> Lightning Component -> modalContent ->

Component displays the contact details usning Lightning Datatable and have many new features. Use below code

modalContent.cmp

[codesyntax lang=”xml” container=”div” doclinks=”1″]

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" 
                controller="Spring18ReleaseController"
                access="global" >
	
    
    <aura:handler name='init' action="{!c.init}" value="{!this}" />
    
    <aura:attribute name="conList" type="Object"/>
    <aura:attribute name="columns" type="List"/>
    <aura:attribute name='showRecordid' type='String' />

    
    <!-- Lightning Datatale Start -->
    <lightning:datatable aura:id="contactTable"
                         data="{!v.conList}" 
                         columns="{!v.columns}" 
                         onsort="{!c.updateColumnSorting}"
                         hideCheckboxColumn="false"
                         onrowaction="{!c.handleRowAction}"
                         keyField="id">
    </lightning:datatable> 
    
</aura:component>

[/codesyntax]

modalContentController.js

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
	init: function (cmp, event, helper) {
        var headerActions = [
            {
                label: 'All',
                checked: true,
                name:'all'
            },
            {
                label: 'Published',
                checked: false,
                name:'show_published'
            },
            {
                label: 'Unpublished',
                checked: false,
                name:'show_unpublished'
            },
        ];
         var actions = helper.getRowActions.bind(this, cmp);
         cmp.set('v.columns', [
             { label: 'Name', fieldName: 'Name', type: 'text', sortable:true },
             { label: 'Title', fieldName: 'Title', type: 'text', sortable:true },
             { label: 'Phone', fieldName: 'Phone', type: 'phone', sortable:true },
             { label: 'Email', fieldName: 'Email', type: 'email', sortable:true },
             { type: 'action', typeAttributes: { rowActions: actions } }
         ]);
          helper.onInit(cmp, event, helper);
     },
     handleRowAction: function (cmp, event, helper) {
        var action = event.getParam('action');
        var row = event.getParam('row');
        switch (action.name) {
            case 'activate':
                helper.activateContact(cmp, row)
                break;
            case 'deactivate':
                helper.deactivateContact(cmp, row);
                break;
            case 'Delete':
                helper.deleteContact(cmp, row);
                break;
            case  'Edit' :
                helper.editContact(cmp, row);
                break;
            case 'Show_Details' :
                helper.showDetails(cmp, row);
                break;
        }
    },
    // Client-side controller called by the onsort event handler
    updateColumnSorting: function (cmp, event, helper) {
        var fieldName = event.getParam('fieldName');
        var sortDirection = event.getParam('sortDirection');
        var tableComp = cmp.find('contactTable');
        // assign the latest attribute with the sorted column fieldName and sorted direction
        tableComp.set("v.sortedBy", fieldName);
        tableComp.set("v.sortedDirection", sortDirection);
        helper.sortData(cmp, fieldName, sortDirection);
    }
})

[/codesyntax]

 

modalContentHelper.js

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
	onInit : function(component, event, helper) {
		var action = component.get('c.fetchContcts');
        action.setCallback(this, function(response){
            var responseValue = response.getReturnValue();
            component.set('v.conList', responseValue);
        });
        $A.enqueueAction(action);
	},
    getRowActions: function (cmp, row, doneCallback) {
        var actions = [];
        actions.push({
            'label': 'Edit',
            'iconName': 'action:edit',
            'name': 'Edit'
        });
        actions.push({
            'label': 'Delete',
            'iconName': 'action:delete',
            'name': 'Delete'
        });
        actions.push({
            'label': 'Show Details',
            'iconName': 'action:preview',
            'name': 'Show_Details'
        });
        /*if (row['isActive__c']) {
            actions.push({
                'label': 'Deactivate',
                'iconName': 'utility:block_visitor',
                'name': 'deactivate'
            });
        } else {
            actions.push({
                'label': 'Activate',
                'iconName': 'utility:adduser',
                'name': 'activate'
            });
        }*/
        // simulate a trip to the server
        setTimeout($A.getCallback(function () {
            doneCallback(actions);
        }), 200);
    },
    activateContact: function (cmp, row) {
        var rows = cmp.get('v.conList');
        var rowIndex = rows.indexOf(row);
        rows[rowIndex]['isActive__c'] = true;
        rows[rowIndex]['active'] = 'Active';
        cmp.set('v.conList', rows);
    },
    deactivateContact: function (cmp, row) {
        var rows = cmp.get('v.conList');
        var rowIndex = rows.indexOf(row);
        rows[rowIndex]['isActive__c'] = false;
        rows[rowIndex]['active'] = 'Inactive';
        cmp.set('v.conList', rows);
    },
    deleteContact : function (component, contact) {
        // Put the logic for deleting the contact Here
    },
    editContact : function (component, contact) {
        // Open the modal to Edit complete Contact in Modal using force:recordEdit
    },
    showDetails : function (component, contact) {
        component.set('v.showRecordid', contact.Id);
    },
    sortData: function (cmp, fieldName, sortDirection) {
        var data = cmp.get("v.conList");
        var reverse = sortDirection !== 'asc';
        //sorts the rows based on the column header that's clicked
        data.sort(this.sortBy(fieldName, reverse))
        cmp.set("v.conList", data);
    },
    sortBy: function (field, reverse, primer) {
        var key = primer ?
            function(x) {return primer(x[field])} :
            function(x) {return x[field]};
        //checks if the two rows should switch places
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
        }
    },
    GetSortOrderAsc : function(component,prop){
        var a=component.get('v.conList');
        return function(a, b) {  
            if (a[prop] > b[prop]) {  
                return 1;  
            } else if (a[prop] < b[prop]) {  
                return -1;  
            }  
            return 0;  
        }  
        
    },
    GetSortOrderDsc : function(component,prop){
        var a=component.get('v.conList');
        return function(a, b) {  
            if (a[prop] > b[prop]) {  
                return -1;  
            } else if (a[prop] < b[prop]) {  
                return 1;  
            }  
            return 0;  
        }  
        
    }
})

[/codesyntax]

 

Step2 – Create modalFooter.cmp File -> New -> lightning Component -> modalFooter ->

This component also use the lightning:overlayLibrary tag and the same aura:id that we will define in mainComponent. For footer component use below code

modalFooter.cmp

[codesyntax lang=”xml” container=”div” doclinks=”1″]

<aura:component >
    <lightning:overlayLibrary aura:id="overlayLib"/>
    <lightning:button name="cancel" label="Cancel" onclick="{!c.handleCancel}"/>
    <lightning:button name="ok" label="OK" variant="brand" onclick="{!c.handleOK}"/>
</aura:component>

[/codesyntax]

 

modalFooterController.js

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
    handleCancel : function(component, event, helper) {
        //closes the modal or popover from the component
        component.find("overlayLib").notifyClose();
    },
    handleOK : function(component, event, helper) {
        //do something
    }
})

[/codesyntax]

 

Step3 – Create mainComponent.cmp File -> New -> lightning Component -> mainComponent ->

This component contains a button by clicking on which a modal will open displaying the Pagination using Lighning Datatable. Use below code for the component

mainComponent.cmp

[codesyntax lang=”xml” container=”div” doclinks=”1″]

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">  
    
    <!-- 
		lightning:overlayLibrary responsible to show/hide the modal using standard Lightning Library 
	-->
    
    <lightning:overlayLibrary aura:id="overlayLib"/>
    
    <!-- lightning:overlayLibrary End-->
    
    <!-- Put a lightning button clicking on which modal will open End -->
    
    <lightning:button name="modal" label="Show Modal" onclick="{!c.handleShowModal}"/>
    
    <!-- Put a lightning button clicking on which modal will open End -->
    
</aura:component>

[/codesyntax]

 

mainComponentController.js

In the controller, we are using $A.createComponents to create the components(multiple) dynamically so that it will also improve the performance of the Lightning Components.

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
    handleShowModal : function (component, event, helper) {
        var modalBody;
        var modalFooter;
        $A.createComponents([
            ["c:modalContent",{}],
            ["c:modalFooter",{}]
        ],
                            function(components, status){
                                if (status === "SUCCESS") {
                                    modalBody = components[0];
                                    modalFooter = components[1];
                                    component.find('overlayLib').showCustomModal({
                                        header: "Paginaniton In Lightning",
                                        body: modalBody, 
                                        footer: modalFooter,
                                        showCloseButton: true,
                                        cssClass: "my-modal,my-custom-class,my-other-class",
                                        closeCallback: function() {
                                            
                                        }
                                    });
                                }
                            }
                           );
    }
})

[/codesyntax]

Step4 – Create an Apex Class Spring18ReleaseController.

[codesyntax lang=”java” container=”div” doclinks=”1″]

public class Spring18ReleaseController {
	@AuraEnabled
    public static List<Contact> fetchContcts(){
        List<Contact> contactList = new List<Contact>();
        contactList = [Select Id, Name, Email, Title, Phone, isActive__c From Contact Limit 5];
        return contactList;
    }
    public class lightningTableColumnWrapper{
        @AuraEnabled
        public  String label { get; set; }
        @AuraEnabled 
        public  String fieldName { get; set; }
        @AuraEnabled
        public String type { get; set; }
        @AuraEnabled
        public Boolean sortable	{ get; set; }
    }
    public class lightningTableDataWrapper{
        @AuraEnabled
        public Contact con { get; set; }
        @AuraEnabled
        public String accountName { get; set; }
    }
    
    public class lightningTableWrapper{
        @AuraEnabled
        public List<lightningTableColumnWrapper> tableColumnWrapper { get; set; }
        @AuraEnabled
        public List<lightningTableDataWrapper> tableDataWrapper { get; set; }
    }
}

[/codesyntax] 

Step5 – Add mainComponent.cmp component into any Object Detail Page to see the output. In the example, I will add to the Account Detail page.

Output: –  See the output below

Custom_Modal

 

Please let me know if you do have any issue/suggestion. You can let us your thoughts into the comment section or you can tweet me here.

Resources: – 

Salesforce Document

 

 

Amit Singh
Amit Singhhttps://www.pantherschools.com/
Amit Singh aka @sfdcpanther/pantherschools, a Salesforce Technical Architect, Consultant with over 8+ years of experience in Salesforce technology. 21x Certified. Blogger, Speaker, and Instructor. DevSecOps Champion
Share this

Leave a review

Excellent

SUBSCRIBE-US

Book a 1:1 Call

Must-read

How to Utilize Salesforce CLI sf (v2)

The Salesforce CLI is not just a tool; it’s the cornerstone of development on the Salesforce Platform. It’s your go-to for building, testing, deploying, and more. As one of the most important development tools in our ecosystem

Save the day of a Developer with Apex Log Analyzer

Table of Contents What is Apex Log Analyzer? Apex Log Analyzer, a tool designed with Salesforce developers in mind, is here to simplify and accelerate your...

Salesforce PodCast

Introduction Hey Everyone, Welcome to my podcast, the first-ever podcast in India for Salesforce professionals. Achievement We are happy to announce that we have been selected as Top...

Recent articles

More like this

12 COMMENTS

  1. I am trying to pass attributes while creating component but 2 way binding is not working.

    $A.createComponents([
    [“c:GRG_MeetingAppraochModal”,{
    ‘record’ : component.get(“v.record”),
    ‘recordId’ : component.get(“v.recordId”),
    ‘selectedAppraoches’ : component.get(“v.selectedAppraoches”)
    }],
    [“c:GRG_MeetingAppraochFooterModal”,{
    ‘record’ : component.get(“v.record”),
    ‘recordId’ : component.get(“v.recordId”),
    ‘selectedAppraoches’ : component.get(“v.selectedAppraoches”)
    }]
    ],
    function(components, status) {
    if (status === “SUCCESS”) {
    modalBody = components[0];
    modalFooter = components[1];
    component.find(‘overlay-modal’).showCustomModal({
    header: “Approach List”,
    body: modalBody,
    footer: modalFooter,
    showCloseButton: true,
    cssClass: “approach-modal slds-modal_large”,
    closeCallback: function(){
    console.log(components[0].get(“v.selectedAppraoches”)); /// I can’t see values which user has selected in modal. It shows me undefined.
    console.log(components[0].get(“v.recordId”)); /// I even can’t see values for recordId also.
    }
    });
    }
    });

    • Hi Saurabh,

      What are you trying to achieve here? closeCallback method called when you are closing the Modal.

      Please let me know so that I can help you in the best way.

      Regards,
      SFDCPanther

  2. I am trying to use this exactly the same but instead of in a lightning record page I want to use it in an aura:application. It wont work though. Any idea why?

    This works great within lightning record page.

    Also, if I call this component from a quick action it works but it throws an error after I close it.

  3. Is it possible to update the Footer of the lightning overlay component? I’d like to update the buttons on the footer, and not sure if I can access the Header, footer, etc.

  4. Hi Amit, I’ve a form in Overlay body as a different component and I’ve created a component for overlay footer with 2 buttons close and submit. On click of submit I want all form fields data, how to get it?

    • Hi Manish,

      You can follow the below approach.

      1 – Create a Lightning Standalone Application
      2 – Put Body and Footer Component inside this Application
      3 – Create an Application Event with Required Attribute
      4 – Fire the event from the component which is having the records
      5 – Handle the Event in the Footer Component
      6 – Now, you will have the data in Footer component and now call the Apex method pass the params and save the data into apex.

      Hope this will help 🙂
      Thanks,
      SFDCPanther

LEAVE A REPLY

Please enter your comment!
Please enter your name here

5/5

Stuck in coding limbo?

Our courses unlock your tech potential