Hey Users,
Creating multiple rows at once is a common testing practice and same it is painful as every time one needs to go to dev. console and create rows by code. With this component, you can dynamically add/delete rows simultaneously with a button click. You can also save the rows and put validations.
This component is very useful when you need to create many rows at once. Currently, this component runs for the Account object & fields. You can update the code to work for any Custom object and fields. You can put validations too.
Component Features:
- Single component
- Add Single Row or Up to 10 rows on a button click
- Disable/Clone a Row
- Save Rows to Salesforce
- Toast Message for Success or error if any.
Demo GIF:
Also Check, Custom Lookup using Aura Lightning
Also Check, Multi-Select Lookup Lightning Component
Step-1: Create an Apex controller
Our apex controller will save the records on passing data to it. Here we are using a wrapper class to return message and messageType to check if successful or not.
Create DynamicRowController.cls Apex class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header */ public with sharing class DynamicRowController { @AuraEnabled public static ResultWrapper saveAccountRecords( String dynamicRowsList ) { ResultWrapper resultWrapper = new ResultWrapper(); try { List<Account> accountRecordsList = (List<Account>) JSON.deserialize(dynamicRowsList, List<Account>.class); insert accountRecordsList; resultWrapper.messageType = 'success'; } catch(Exception err) { resultWrapper.message = err.getMessage(); resultWrapper.messageType = 'error'; } return resultWrapper; } public class ResultWrapper { @AuraEnabled public String message; @AuraEnabled public String messageType; } } /* Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE */ |
Step-2: Create the DynamicRowCreation Lightning Component.
For creating a new Lightning Component, go to dev. console > File > New > Lightning Component.
Now open the DynamicRowCreation.cmp file and paste the below code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
<!-- Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header --> <aura:component controller="DynamicRowController" > <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> <aura:attribute name="numberOfRows" type="Integer" description="Number of Rows to add dynamically" access="private" default="1" /> <aura:attribute name="dynamicRowsList" type="List" description="List of Rows that are created dynamically" access="private" /> <aura:attribute name="message" type="String" default="" description="Success or error message" access="private" /> <aura:attribute name="messageType" type="String" description="Success or error message type" access="private" /> <div class="slds-grid slds-wrap"> <div class="slds-col slds-size_1-of-1 slds-large-size_2-of-12"></div> <div class="slds-col slds-size_1-of-1 slds-large-size_8-of-12"> <aura:if isTrue="{!!empty(v.message)}" > <div class="slds-notify_container slds-is-relative"> <div class="{! 'slds-notify slds-notify_toast slds-theme_' + v.messageType }" role="alert"> <div class="slds-notify__content"> <h2 class="slds-text-heading_small">{!v.message}</h2> </div> </div> </div> </aura:if> <div class="headerDiv slds-grid slds-wrap slds-p-around_x-small"> <div class="slds-col slds-size_1-of-1 slds-medium-size_4-of-12"> <p class="slds-p-top_x-small">Dynamic Row Creation</p> </div> <div class="slds-col slds-size_1-of-1 slds-medium-size_8-of-12"> <div class="multipleRowsDiv"> <span>Add Rows : </span> <lightning:buttonIcon disabled="{!v.numberOfRows == 1}" iconName="utility:dash" variant="border-filled" onclick="{!c.decrementRow}" /> <span> {!v.numberOfRows} </span> <lightning:buttonIcon disabled="{!v.numberOfRows >= 10}" iconName="utility:add" variant="border-filled" onclick="{!c.incrementRow}" /> <lightning:button name="multipleRows" variant="Neutral" label="Go" onclick="{!c.addRow}" /> <lightning:button variant="Neutral" label="SAVE" onclick="{!c.saveRecords}" /> </div> </div> </div> <table class="slds-table slds-table_bordered slds-table_cell-buffer slds-table_col-bordered slds-table_striped outerTable" > <thead> <tr class="slds-text-title_caps"> <th scope="col" style="width:10%" title="S.No.">S.No.</th> <th scope="col" style="width:25%" title="Account Name">Account Name</th> <th scope="col" style="width:25%" title="Account Number">Account Number</th> <th scope="col" style="width:25%" title="Phone">Phone</th> <th scope="col" style="width:15%" title="Actions">Action</th> </tr> </thead> <tbody> <aura:iteration items="{!v.dynamicRowsList}" var="eachRow" indexVar="index" > <tr> <th scope="row">{! index + 1 }</th> <td title="Account Name"> <lightning:input aura:id="dynamiRowsForm" disabled="{! if(eachRow.editMode,false,true) }" class="nameInput" required="true" maxlength="80" name="accName" value="{!eachRow.Name}" /> </td> <td title="AccountNumber"> <lightning:input aura:id="dynamiRowsForm" disabled="{! if(eachRow.editMode,false,true) }" maxlength="50" name="accNumber" value="{!eachRow.AccountNumber}" /> </td> <td title="Phone"> <lightning:input aura:id="dynamiRowsForm" type="tel" disabled="{! if(eachRow.editMode,false,true) }" maxlength="10" name="input3" value="{!eachRow.Phone}" /> </td> <td class="slds-p-around_xx-small"> <lightning:buttonIcon name="{! eachRow.rowNumber }" alternativeText="edit" variant="container" class="{! if(eachRow.editMode,'editClass','nonEditClass') }" iconName="utility:edit" onclick="{!c.editRow}"/> <lightning:button name="{! eachRow.rowNumber }" variant="brand" class="cloneClass" label="Clone" onclick="{!c.cloneRow}"/> <aura:if isTrue="{! eachRow.rowNumber == 1 }" > <lightning:buttonIcon alternativeText="add" variant="container" class="successClass" iconName="utility:add" onclick="{!c.addRow}"/> </aura:if> <aura:if isTrue="{! eachRow.rowNumber > 1 }" > <lightning:buttonIcon name="{! eachRow.rowNumber }" alternativeText="delete" variant="container" class="errorClass" iconName="utility:close" onclick="{!c.deleteRow}"/> </aura:if> </td> </tr> </aura:iteration> </tbody> </table> </div> </div> </aura:component> <!-- Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE --> |
Now open the controller file DynamicRowCreationController.js and paste the below code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/* Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header */ ({ doInit : function(component, event, helper) { helper.addRowHelper(component, 1); }, addRow : function(component, event, helper) { var loopVar = 1; if(event.getSource().get('v.name') === 'multipleRows' && !$A.util.isEmpty('v.numberOfRows') ) { loopVar = component.get('v.numberOfRows') > 10 ? 0 : component.get('v.numberOfRows'); } helper.addRowHelper(component, loopVar); }, editRow : function(component, event, helper) { var dynamicRowsList = component.get("v.dynamicRowsList"); var index = dynamicRowsList.findIndex(x => x.rowNumber === event.getSource().get('v.name')) if(index != -1) dynamicRowsList[index].editMode = !dynamicRowsList[index].editMode; component.set("v.dynamicRowsList", dynamicRowsList); }, deleteRow : function(component, event, helper) { var dynamicRowsList = component.get("v.dynamicRowsList"); var index = dynamicRowsList.findIndex(x => x.rowNumber === event.getSource().get('v.name')) if(index != -1) dynamicRowsList.splice(index, 1); component.set("v.dynamicRowsList", dynamicRowsList); }, cloneRow : function(component, event, helper) { var dynamicRowsList = component.get("v.dynamicRowsList"); var index = dynamicRowsList.findIndex(x => x.rowNumber === event.getSource().get('v.name')) if(index != -1) { dynamicRowsList.push({ 'Name': dynamicRowsList[index].Name, 'Phone': dynamicRowsList[index].Phone, 'AccountNumber': dynamicRowsList[index].AccountNumber, 'editMode': dynamicRowsList[index].editMode, 'rowNumber':dynamicRowsList.length + 1 }); } component.set("v.dynamicRowsList", dynamicRowsList); }, incrementRow: function(component, event, helper) { var numberOfRows = component.get("v.numberOfRows"); component.set("v.numberOfRows", numberOfRows + 1); }, decrementRow: function(component, event, helper) { var numberOfRows = component.get("v.numberOfRows"); component.set("v.numberOfRows", numberOfRows - 1); }, saveRecords : function(component, event, helper) { var validExpense = component.find('dynamiRowsForm').reduce(function ( validSoFar, inputCmp ) { inputCmp.showHelpMessageIfInvalid(); return validSoFar && inputCmp.get('v.validity').valid; }, true); if(validExpense){ helper.saveRecordsHelper(component, event, helper); } }, }) /* Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE */ |
Open the helper file DynamicRowCreationHelper.js and paste the below code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/* Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header */ ({ addRowHelper : function(component, noOfRows) { for(var index = 0; index < noOfRows; index++) { var dynamicRowsList = component.get("v.dynamicRowsList"); dynamicRowsList.push({ 'Name': '', 'Phone': '', 'AccountNumber': '', 'editMode': true, 'rowNumber':dynamicRowsList.length + 1 }); component.set("v.dynamicRowsList", dynamicRowsList); } }, saveRecordsHelper : function(component, event, helper) { var action = component.get('c.saveAccountRecords'); action.setParams({ 'dynamicRowsList' : JSON.stringify(component.get('v.dynamicRowsList')) }); action.setCallback(this,function(response){ var result = response.getReturnValue(); if(response.getState() === 'SUCCESS') { if(result.messageType === 'success') { var dynamicRowList = []; dynamicRowList.push({ 'Name': '', 'Phone': '', 'AccountNumber': '', 'editMode': true, 'rowNumber':dynamicRowList.length + 1 }); component.set("v.dynamicRowsList", dynamicRowList); component.set('v.messageType','success'); component.set('v.message','Records created succesfully'); } else { component.set('v.messageType','error'); component.set('v.message',result.message); } } else { component.set('v.messageType','error'); component.set('v.message','No response from server or client is offline.'); } }); $A.enqueueAction(action); setTimeout($A.getCallback(function() { component.set('v.message',''); }), 5000); }, }) /* Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE */ |
Now finally paste the CSS in the DynamicRowCreation.css file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
.THIS .outerTable { box-shadow: 1px 1px 10px -5px; } .THIS .headerDiv { background-color: #12126d; color: #fff; } .THIS .errorClass { color: white !important; background-color: #e45651; border: 1px solid #fff; } .THIS .editClass { color: white !important; background-color: #556a75; border: 1px solid #fff; } .THIS .nonEditClass { border: 1px solid #fff; background-color: #ddd; } .THIS .successClass { color: white !important; background-color: green; border: 1px solid #fff; } .THIS .cloneClass { border: 1px solid #fff; } .THIS .nameInput input { border-left: 3px solid rgb(194, 57, 52); } .THIS .nameInput .slds-form-element__label { display: none; } .THIS .multipleRowsInput { width: 70px; } |
Step-3: Now we will call the component.
1 2 3 4 5 6 |
<!-- Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header --> <aura:application extends="force:slds"> <c:DynamicRowCreation></c:DynamicRowCreation> </aura:application> |
Finally, our component is ready to use. You can call this component in any Parent component and you can dynamically add/delete rows. You can also call this component on the VF page and put on the Record Detail page.
To call the Lightning component on VF Page – Click Here
Also Check:
For any queries or suggestions/queries, comment below.
Cheers … Happy Coding … 🙂
Hello thanks for this article this is really helpful , but i want add extra functionality to this where i want to remove “+” icon should be remove after adding next section “. how can i achieve that ?