This is the third post in Lightning Tutorial Series and in this post, you'll learn about how to delete the records displayed using a lightning component. In this post, I'll be extending the code used in my first and second post so, if you are just starting or need to learn only about deleting records only do have a look at my previous posts or at least the code by having a look at my blog posts starting from here or my github repository code in update branch here.
Let's start by looking at our code files one by one and doing changes to add a delete feature to our component.
1. Apex Controller
As you know, till now we had only two methods in our Apex Controller getContactList - used to fetch contact list from server and saveContactList - which was used to save the records from our lightning component to salesforce. Let's have a look at the below code now:-
// Apex Controller for Contact List Lightning Component public class ContactListController { @AuraEnabled public static List<Contact> getContactList(List<Id> accountIds) { // Getting the list of contacts from where Id is in accountIds List<Contact> contactList = [SELECT Id, FirstName, LastName, Email, Phone, AccountId FROM Contact WHERE AccountId in :accountIds]; // Returning the contact list return contactList; } @AuraEnabled public static Map<String,String> saveContactList(List<Contact> contactList) { // Forming a string map to return response Map<String,String> resultMap = new Map<String,String>(); // Adding try catch for exception handling try { // Updating the Contact List update contactList; // Setting the success status and message in resultMap resultMap.put('status', 'success'); resultMap.put('message', 'Contacts Updated Successfully'); } catch(Exception e) { // Setting the error status and message in resultMap resultMap.put('status', 'error'); resultMap.put('message',e.getMessage()); } // Returning the result string map return resultMap; } @AuraEnabled public static Map<String,String> deleteContactList(List<Id> contactIds) { //Fetching Contacts List<Contact> contactsToDelete = [SELECT Id FROM Contact WHERE Id in :contactIds]; // Forming a string map to return response Map<String,String> resultMap = new Map<String,String>(); // Adding try catch for exception handling try { // Deleting the Contacts delete contactsToDelete; // Setting the success status and message in resultMap resultMap.put('status', 'success'); resultMap.put('message', 'Contacts Deleted Successfully'); } catch(Exception e) { // Setting the error status and message in resultMap resultMap.put('status', 'error'); resultMap.put('message',e.getMessage()); } // Returning the result string map return resultMap; } }As you can see above that I have added one more method in our controller namely - deleteContactList which is responsible for deleting the contact list by getting the ids of contacts which we have to delete. So, as you can see, the deleteContactList method has a single parameter i.e. a list of contact ids and then it is using those list of contact ids to fetch the contacts from the database. I have initialized a map of key and value pairs both of type string and named it resultMap as done previously in the saveContactList method. As I am using the delete keyword to delete the contacts, it only accept an SObject or a SObject List that's why I have done a SOQL query to fetch the contacts related to the ids passed in the method. If you don't want to do the SOQL query, you can pass the list of contacts in the parameter as I have done in save method. But it will be heavier to pass more data to server that's why I have limited it to list of ids only. A more better approach is to use Database.delete method which we'll learn in upcoming tutorials. I have surrounded this query within a try catch so that it can catch any kind of exception and formed a result map in both the cases with key - value pairs of my choice and returned that resultMap as a response.
2. Lightning Component
We are done with the apex controller so let's move on to the lightning component in which we have to do very little changes to make our component capable of deleting record. Let's have a look at the code below:-
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="ContactListController" access="global" > <!-- Handler to call function when page is loaded initially --> <aura:handler name="init" action="{!c.getContactsList}" value="{!this}" /> <!-- List of contacts stored in attribute --> <aura:attribute name="contactList" type="List" /> <!-- Lightning card to show contacts --> <lightning:card title="Contacts"> <!-- Body of lightning card starts here --> <p class="slds-p-horizontal_small"> <!-- Aura iteration to iterate list, similar to apex:repeat --> <div aura:id="recordViewForm"> <aura:iteration items="{!v.contactList}" var="contact"> <!-- recordViewForm to view the record --> <lightning:recordViewForm recordId="{!contact.Id}" objectApiName="Contact"> <div class="slds-box slds-theme_default"> <!-- inputfield checkbox used to check wether to delete the contact or not --> <lightning:input type="checkbox" value="{!contact.Id}" label="Mark for Deletion" aura:id="deleteContact" /> <br /> <!-- outputfield used to output the record field data inside recordViewForm --> <lightning:outputField fieldName="FirstName" /> <lightning:outputField fieldName="LastName" /> <lightning:outputField fieldName="Email" /> <lightning:outputField fieldName="Phone" /> </div> </lightning:recordViewForm> <!-- Line break between two records --> <br /> </aura:iteration> </div> <div aura:id="recordEditForm" class="formHide"> <aura:iteration items="{!v.contactList}" var="contact"> <div class="slds-box slds-theme_default"> <!-- inputfield used to update the record field data --> <lightning:input value="{!contact.FirstName}" /> <lightning:input value="{!contact.LastName}" /> <lightning:input type="email" value="{!contact.Email}" /> <lightning:input type="tel" value="{!contact.Phone}" pattern="\([0-9]{3}\) [0-9]{3}-[0-9]{4}" /> </div> <br /> <!-- Line break between two records --> </aura:iteration> </div> </p> <!-- Lightning card actions --> <aura:set attribute="actions"> <!-- Delete button added --> <lightning:button variant="destructive" label="Delete" onclick="{!c.deleteContacts}" /> <!-- New button added --> <lightning:button label="New" onclick="{!c.newContact}" /> <!-- Edit/Save button added --> <lightning:button variant="brand" label="Edit" name="edit" onclick="{!c.editContacts}" /> </aura:set> </lightning:card> </aura:component>As you can see, in the recordView form, I have added a lightning:input tag with the following attributes:-
- type="checkbox"
- value="{!contact.Id}" ( As the input tag is under aura:iteration with eachrecord referred by var contact )
- label="Mark for Deletion"
- aura:id="deleteContact"
- checked ( has a type boolean and will be true if checkbox is checked. No need to define and give an initial value to this )
So, each record will display a checkbox with label as - Mark for Deletion that means you want to mark this contact for deletion or not ? and finally we add a button that will delete all the contacts that are marked for deletion on click of a delete button that I have added in the lightning card actions section with variant as destructive, label as delete and will call the controller method deleteContacts on clicking of this button.
3. Lightning Controller
Moving on to our lightning controller, as my button is calling the deleteContacts controller method on click, so I have to define the same in my controller. Here is the code for the lightning controller:-
({ // Function called on initial page loading to get contact list from server getContactsList : function(component, event, helper) { // Helper function - fetchContacts called for interaction with server helper.fetchContacts(component, event, helper); }, // Function used to create a new Contact newContact: function(component, event, helper) { // Global event force:createRecord is used var createContact = $A.get("e.force:createRecord"); // Parameters like apiName and defaultValues are set createContact.setParams({ "entityApiName": "Contact", "defaultFieldValues": { "AccountId": component.get("v.recordId") } }); // Event fired and new contact dialog open createContact.fire(); }, // Function used to update the contacts editContacts: function(component, event, helper) { // Getting the button element var btn = event.getSource(); // Getting the value in the name attribute var name = btn.get('v.name'); // Getting the record view form and the record edit form elements var recordViewForm = component.find('recordViewForm'); var recordEditForm = component.find('recordEditForm'); // If button is edit if(name=='edit') { // Hiding the recordView Form and making the recordEdit form visible $A.util.addClass(recordViewForm,'formHide'); $A.util.removeClass(recordEditForm,'formHide'); // Changing the button name and label btn.set('v.name','save'); btn.set('v.label','Save'); } else if(name=='save') { // Calling saveContacts if the button is save helper.saveContacts(component, event, helper); } }, // Function used to delete the contacts deleteContacts: function(component, event, helper) { // Calling removeContacts Helper Function helper.removeContacts(component, event, helper); } })
You can see above that the last function is deleteContacts that is only calling the helper's removeContacts method as all the server related work, I prefer to do in helper so here also on clicking of delete button all the operations take place in helper.
4. Lightning Helper
Moving on to out lightning helper now, let's have a look at the code first and then discuss things one by one:-
({ // Function to fetch data from server called in initial loading of page fetchContacts : function(component, event, helper) { // Assign server method to action variable var action = component.get("c.getContactList"); // Getting the account id from page var accountId = component.get("v.recordId"); // Setting parameters for server method action.setParams({ accountIds: accountId }); // Callback function to get the response action.setCallback(this, function(response) { // Getting the response state var state = response.getState(); // Check if response state is success if(state === 'SUCCESS') { // Getting the list of contacts from response and storing in js variable var contactList = response.getReturnValue(); // Set the list attribute in component with the value returned by function component.set("v.contactList",contactList); } else { // Show an alert if the state is incomplete or error alert('Error in getting data'); } }); // Adding the action variable to the global action queue $A.enqueueAction(action); }, // Function to update the contacts on server saveContacts: function(component, event, helper) { // Getting the contact list from lightning component var contactList = component.get("v.contactList"); // Getting the recordViewForm and recordEditForm component var recordViewForm = component.find('recordViewForm'); var recordEditForm = component.find('recordEditForm'); // Initializing the toast event to show toast var toastEvent = $A.get('e.force:showToast'); // Defining the action to save contact List ( will call the saveContactList apex controller ) var saveAction = component.get("c.saveContactList"); // setting the params to be passed to apex controller saveAction.setParams({ contactList: contactList }); // callback action on getting the response from server saveAction.setCallback(this, function(response) { // Getting the state from response var state = response.getState(); if(state === 'SUCCESS') { // Getting the response from server var dataMap = response.getReturnValue(); // Checking if the status is success if(dataMap.status=='success') { // Remove the formHide class $A.util.removeClass(recordViewForm,'formHide'); // Add the formHide class $A.util.addClass(recordEditForm,'formHide'); // Getting the button element var btn = event.getSource(); // Setting the label and name of button back to edit btn.set('v.name','edit'); btn.set('v.label','Edit'); // Setting the success toast which is dismissable ( vanish on timeout or on clicking X button ) toastEvent.setParams({ 'title': 'Success!', 'type': 'success', 'mode': 'dismissable', 'message': dataMap.message }); // Fire success toast event ( Show toast ) toastEvent.fire(); } // Checking if the status is error else if(dataMap.status=='error') { // Setting the error toast which is dismissable ( vanish on timeout or on clicking X button ) toastEvent.setParams({ 'title': 'Error!', 'type': 'error', 'mode': 'dismissable', 'message': dataMap.message }); // Fire error toast event ( Show toast ) toastEvent.fire(); } } else { // Show an alert if the state is incomplete or error alert('Error in getting data'); } }); $A.enqueueAction(saveAction); }, // Function to delete the contacts from server removeContacts: function(component, event, helper) { // Getting the deleteContact Component var contactsToDelete = component.find("deleteContact"); // Initialize an empty array var idsToDelete = []; // Checking if contactsToDelete is an array if(contactsToDelete.length!=undefined) { // Iterating the array to get contact ids for(var i=0;i<contactsToDelete.length;i++) { // If contact has delete checkbox checked, add contact id to list of ids to delete if(contactsToDelete[i].get("v.checked")) idsToDelete.push(contactsToDelete[i].get("v.value")); } } else { // if contactsToDelete is not an array but single object, // check if delete checkbox is checked and push id to list of ids to delete if(contactsToDelete.get("v.checked")) idsToDelete.push(contactsToDelete.get("v.value")); } // Initializing the toast event to show toast var toastEvent = $A.get('e.force:showToast'); // Defining the action to delete contact List ( will call the deleteContactList apex controller ) var deleteAction = component.get('c.deleteContactList'); // setting the params to be passed to apex controller deleteAction.setParams({ contactIds: idsToDelete }); // callback action on getting the response from server deleteAction.setCallback(this, function(response) { // Getting the state from response var state = response.getState(); if(state === 'SUCCESS') { // Getting the response from server var dataMap = response.getReturnValue(); // Checking if the status is success if(dataMap.status=='success') { // Setting the success toast which is dismissable ( vanish on timeout or on clicking X button ) toastEvent.setParams({ 'title': 'Success!', 'type': 'success', 'mode': 'dismissable', 'message': dataMap.message }); // Fire success toast event ( Show toast ) toastEvent.fire(); window.location.reload(); } // Checking if the status is error else if(dataMap.status=='error') { // Setting the error toast which is dismissable ( vanish on timeout or on clicking X button ) toastEvent.setParams({ 'title': 'Error!', 'type': 'error', 'mode': 'dismissable', 'message': dataMap.message }); // Fire error toast event ( Show toast ) toastEvent.fire(); } } else { // Show an alert if the state is incomplete or error alert('Error in getting data'); } }); $A.enqueueAction(deleteAction); } })
As you can see above, I have added one method namely - removeContacts at the last which is responsible for deleting the marked contacts. First of all this method is getting the component which has an aura:id of deleteContact. If component.find() finds a single component with the aura:id given then it returns that component, otherwise it returns an array of components. So we have initialized an empty array namely idsToDelete which will store the ids that we have to pass to our apex controller to delete the contacts. Then we are checking that whether the returned value of our component.find or the contactsToDelete variable is array or not. So, in javascript we can always get the length of array using [].length and if the length is undefined that means that I have a single component (single contact record) not an array.
If I have a single contact record, the else condition will be executed and I'll check that whether my checked attribute is true or false and if it's true I'll add that component to the list of ids ( array ) which is to be passed to apex controller for deletion and if I have multiple contact records, I'll traverse through the array and do exactly the same thing. This was the whole logic and the rest part is same as in saveContacts method, I have initialized a toast, and then a deleteAction which is calling the deleteContactList apex controller's method. I am passing the idsToDelete array to the method's param contactIds . In the callback function I am checking the state, if the state is success and the dataMap has also the status of success, then fire a success toast and reload the page using window.location.reload and if the state is success but status is error, then we'll fire the error toast event with the exception or error message displayed from dataMap. Otherwise if the state is not success, then I have simply displayed an alert - error in getting data. Finally I have added this action to the global action queue using $A.enqueueAction().
Congratulations..!! You have added the functionality to delete the record in your lightning component too and your final component looks like this:-
Tired of reading or just scrolled down..!! Don't worry, you can watch the video too.
If you liked this post then do follow, subscribe, comment your views or any feedback and share it with everyone in your circle so that they can also get benefit by this. Hope to see you next time too when we'll learn how to create new records using our lightning component and custom ui instead of calling standard create record global event. For the whole code at one place, please refer to my github repository here. You can fork this repo and do your own changes. However please switch to delete branch to get the code specified here and not the future changes. Otherwise, directly go to the delete branch by clicking here.
Let's move on to Lightning Tutorial Part 4 now in which you'll learn how you can create a new record using your own custom lightning component and save the record to salesforce.
Happy Trailblazing..!!
If I have a single contact record, the else condition will be executed and I'll check that whether my checked attribute is true or false and if it's true I'll add that component to the list of ids ( array ) which is to be passed to apex controller for deletion and if I have multiple contact records, I'll traverse through the array and do exactly the same thing. This was the whole logic and the rest part is same as in saveContacts method, I have initialized a toast, and then a deleteAction which is calling the deleteContactList apex controller's method. I am passing the idsToDelete array to the method's param contactIds . In the callback function I am checking the state, if the state is success and the dataMap has also the status of success, then fire a success toast and reload the page using window.location.reload and if the state is success but status is error, then we'll fire the error toast event with the exception or error message displayed from dataMap. Otherwise if the state is not success, then I have simply displayed an alert - error in getting data. Finally I have added this action to the global action queue using $A.enqueueAction().
Congratulations..!! You have added the functionality to delete the record in your lightning component too and your final component looks like this:-
Tired of reading or just scrolled down..!! Don't worry, you can watch the video too.
If you liked this post then do follow, subscribe, comment your views or any feedback and share it with everyone in your circle so that they can also get benefit by this. Hope to see you next time too when we'll learn how to create new records using our lightning component and custom ui instead of calling standard create record global event. For the whole code at one place, please refer to my github repository here. You can fork this repo and do your own changes. However please switch to delete branch to get the code specified here and not the future changes. Otherwise, directly go to the delete branch by clicking here.
Let's move on to Lightning Tutorial Part 4 now in which you'll learn how you can create a new record using your own custom lightning component and save the record to salesforce.
Appreciate your work.! Nice & easy way to learn LEX Dev.
ReplyDeleteThank you so much for your appreciation... :-)
DeleteThis really motivates me to do more for the community.
Rahul excellent work buddy..
ReplyDeleteHappy to see that you liked it Ershad :-) Do share it in your network so that others can get benefit too.
DeleteSuperb Article
ReplyDeleteThank you :-) share it among others too.
DeleteHi you are doing great work . please post articles on Apex Integrations and Callouts .
ReplyDeleteHi, Thanks for your suggestion. Please add it here too:- https://sfdcstop.blogspot.com/p/what-do-you-want-us-to-post-next.html Do share these tutorials in your network too :-)
DeleteNice job Rahul. Following all your blogs. It helped me to learn a lot.
ReplyDeleteGood to know that it helped Ankit. Make sure to share it in your network too :-)
DeleteNice job Rahul, Following all your blogs/videos. It help me to learn a lot. Really appreciate all your effort.
ReplyDeleteGood to know that it helped Ankit. Make sure to share it in your network too :-)
DeleteAwesome work Rahul!!
ReplyDeleteThanks buddy :-) share it among your peers too.
DeleteSuperb work,
ReplyDeletebut I have one concern if unselect the checkbox it will be display message please select checkbox.How to do please sugget me.
Hi Krishna,
DeleteYou just need to check if idsToDelete array is empty before firing the delete action. Just add a check like:- if(idsToDelete.length==0) { fire a toast with message:- please select a checkbox }
Hi Rahul,
ReplyDeletesuperb article, but i have one concern if not select checkbox it will display alert message.How to do please suggest me.
Thanks Krishna :-) Make sure to share it among your network too. Please see my comment above for your query.
DeleteThank You so much for posting such amazing content. It really helped me a lot :-))
ReplyDelete