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

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

<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>

modalContentController.js

({
    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);
    }
})

 

modalContentHelper.js

({
    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;  
        }  
       
    }
})

 

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

<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>

 

modalFooterController.js

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

 

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

<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>

 

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.

({
    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() {
                                           
                                        }
                                    });
                                }
                            }
                           );
    }
})

Step4 – Create an Apex Class Spring18ReleaseController.

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; }
    }
}

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

 

 

10 thoughts on “How to Create Modals in Lightning Component (lightning:overlayLibrary)

  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.
    }
    });
    }
    });

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *