Dynamic Add Delete Row In Lightning Component

In below example I’m adding account record in Salesforce Lightning Component with Add Delete Rows functionality.

Here I’ve created 2 lightning components and 2 lightning events, by these lightning components you can create multiple records as your requirement by Adding or Deleting new rows.

Step1 : Create Lightning Event “AddRowEvent.evt”

<aura:event type="COMPONENT" description="Event to add row"></aura:event>

Step2 : Create Lightning Event “DeleteRowEvent.evt”

<aura:event type="COMPONENT" description="Event to delete row" >
    <aura:attribute name="indexVar" type="Integer" description="Use For Delete Row" />
</aura:event>

Step3 : Create Lightning Component “Child.cmp”.

<!--Child-->
<aura:component >    
    <!--Attribute for store single Account object instance--> 
    <aura:attribute name="AccountInstance" type="Account"/>
    <!--Attribute for store Index of Particular Instance --> 
    <aura:attribute name="rowIndex" type="String"/>
    
    <!-- Register 2 Lightning Event for handle add or Delete rows on Parent Component  --> 
    <aura:registerEvent name="DeleteRowEvent" type="c:DeleteRowEvent"/> 
    <aura:registerEvent name="AddRowEvent" type="c:AddRowEvent"/> 
    
    <!-- Table Row -->   
    <tr class="slds-text-title_caps">
        <td> 
            {!v.rowIndex + 1}
        </td>
        <td>
            <ui:inputText class="slds-input" value="{!v.AccountInstance.Name}"/>
        </td>
        <td>
            <ui:inputText class="slds-input" value="{!v.AccountInstance.AccountNumber}"/>
        </td>
        <td>
            <ui:inputPhone class="slds-input" value="{!v.AccountInstance.Phone}"/>
        </td>
        <td>
            <!-- conditionally Display Add or Delete Icons, if rowIndex is 0 then show add row Icon else show delete Icon--> 
            <aura:if isTrue="{!v.rowIndex == 0}">
                <a onclick="{!c.addRow}">
                    <lightning:icon iconName="utility:add" class="slds-icon slds-icon_small" size="small" alternativeText="Add"/>
                    <span class="slds-assistive-text">Add</span>
                </a>    
                <aura:set attribute="else">
                    <a onclick="{!c.deleteRow}">
                        <lightning:icon variant="error" iconName="utility:delete" class="slds-icon slds-icon_small" size="small" alternativeText="Delete"/>
                        <span class="slds-assistive-text">Delete</span>
                    </a>
                </aura:set> 
            </aura:if>
        </td> 
    </tr>
</aura:component>

Step4 : Add “Child.cmp” controller “ChildController.js” method.

  • “addRow” method to add row.
  • “deleteRow” method to delete row.
({
    addRow : function(component, event, helper){
        //Execute the AddRowEvent Lightning Event 
        component.getEvent("AddRowEvent").fire();     
    },
    
    deleteRow : function(component, event, helper){
        //Execute the DeleteRowEvent Lightning Event and pass the deleted Row Index to Event attribute
        component.getEvent("DeleteRowEvent").setParams({"indexVar" : component.get("v.rowIndex") }).fire();
    }, 
    
})

Step5 : Create Apex Controller “AuraSampleController.apxc”.

  • “SaveAccounts” method to save list of Account.
public with sharing class AuraSampleController{
   @AuraEnabled
    public static void SaveAccounts(List<Account> accList){
        Insert accList;
    }
}

Step6 : Create Lightning Component “Parent.cmp”

<!--Parent-->
<aura:component controller="AuraSampleController" Implements="flexipage:availableForRecordHome,force:hasRecordId">
    <!--Init handler which is call doInit js function on component Load-->  
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <!--Event handler for Add and Delete Row Event which is execute from Child Component-->    
    <aura:handler name="DeleteRowEvent" event="c:DeleteRowEvent" action="{!c.removeDeletedRow}"/>
    <aura:handler name="AddRowEvent" event="c:AddRowEvent" action="{!c.addRow}"/>
    
    <!--Aura Attribute for store Account Object List as Array-->    
    <aura:attribute name="AccountList" type="Account[]"/> 
    
    <!--Header Part-->        
    <div class="slds-page-header">
        <h1 class="slds-page-header__title">Create Multiple Accounts in Lightning</h1>
    </div>
    
    <!--Table Part-->           
    <table class="slds-table slds-table_bordered slds-table_cell-buffer"> 
        <thead>
            <tr class="slds-text-title_caps">
                <th scope="col">
                    <div class="slds-truncate">#</div>
                </th>
                <th scope="col">
                    <div class="slds-truncate" title="Account Name">Account Name</div>
                </th>
                <th scope="col">
                    <div class="slds-truncate" title="Account Number">Account Number</div>
                </th>
                <th scope="col">
                    <div class="slds-truncate" title="Phone">Phone</div>
                </th>
                 <th scope="col">
                    <div class="slds-truncate" title="Action">Action</div>
                </th>
            </tr>
        </thead>   
        <tbody>
            <!--Iterate the child Component for display Table rows 
               with pass the List Item Index for track the Every child Component 
               and pass each List Account Instance -->         
            <aura:iteration items="{!v.AccountList}" var="item" indexVar="index">
                <c:Child AccountInstance="{!item}" rowIndex="{!index}" />
            </aura:iteration>
        </tbody>
    </table>
    <br/>
    <!--Save Button which is call Save js function on click --> 
    <button class="slds-button slds-button_brand"  onclick="{!c.Save}">Save</button>
</aura:component>

Step7 : Add “Parent.cmp” controller “ParentController.js” method.

  • “doInit” method for component load.
  • “Save” method for data Save.
  • “addRow” method to add new row.
  • “removeDeletedRow” method to remove delete row from list.
({
    
    // function call on component Load
    doInit: function(component, event, helper) {
        // create a Default RowItem [Account Instance] on first time Component Load
        // by call this helper function  
        helper.createObjectData(component, event);
    },
    
    // function for save the Records 
    Save: function(component, event, helper) {
        // first call the helper function in if block which will return true or false.
        // this helper function check the "Account Name" will not be blank on each row.
        if (helper.validate(component, event)) {
            // call the apex class method for save the Account List
            // with pass the contact List attribute to method param.  
            var action = component.get("c.SaveAccounts");
            action.setParams({
                "accList": component.get("v.AccountList")
            });
            // set call back 
            action.setCallback(this, function(response) {
                var state = response.getState();
                if (state === "SUCCESS") {
                    // if response if success then reset the 'AccountList' Attribute 
                    // and call the common helper method for create a default Object Data to Account List 
                    component.set("v.AccountList", []);
                    helper.createObjectData(component, event);
                    alert('Account records saved successfully');
                }
            });
            // enqueue the server side action  
            $A.enqueueAction(action);
        }
    },
    
    // function for create new object Row in Contact List 
    addRow: function(component, event, helper) {
        // call the comman "createObjectData" helper method for add new Object Row to List  
        helper.createObjectData(component, event);
    },
    
    // function for delete the row 
    removeDeletedRow: function(component, event, helper) {
        //get the selected row Index for delete, from Lightning Event Attribute  
        var index = event.getParam("indexVar");
        //get the all List (AccountList attribute) and remove the Object Element Using splice method    
        var AllRowsList = component.get("v.AccountList");
        AllRowsList.splice(index, 1);
        //set the AccountList after remove selected row element  
        component.set("v.AccountList", AllRowsList);
    },
})

Step8 : Add “Parent.cmp” component helper “ParentHelper.js” method.

  • “createObjectData” method for create new instance of Account object.
  • “validate” method to validate Account Name.
({
    createObjectData: function(component, event) {
        //get the AccountList from component and add(push) New Object to List  
        var RowItemList = component.get("v.AccountList");
        RowItemList.push({
            'sobjectType': 'Account',
            'Name': '',
            'AccountNumber': '',
            'Phone': ''
        });
        // set the updated list to attribute (AccountList) again    
        component.set("v.AccountList", RowItemList);
    },
    //helper function for check if Account Name is not null/blank on save  
    validate: function(component, event) {
        var isValid = true;
        var allAccountRows = component.get("v.AccountList");
        for (var indexVar = 0; indexVar < allAccountRows.length; indexVar++) {
            if (allAccountRows[indexVar].Name == '') {
                isValid = false;
                alert('Account Name Cannot be blank on row number ' + (indexVar + 1));
            }
        }
        return isValid;
    },
})

Step9 : Create Lightning App “MyDemo.app”.

<!--MyDemo-->
<aura:application implements="force:appHostable" extends="force:slds">
    <c:Parent />    
</aura:application>

Output :

Create Account Record :

Created Account Records :

Multiple Conditions in aura:if on Lightning Component

aura:if evaluates the isTrue expression on the server and instantiates components in either its body or else attribute. Only one branch is created and rendered. Switching condition unrenders and destroys the current branch and generates the other.

In aura:if tag we can not use &&,AND,||,OR operator for multiple conditions in isTrue attribute, but we can use logical functions in aura:if tag like or(), and().

Here is the example how to use multiple Boolean conditions in aura:if tag.

Lightning Component:

<!--TestComponent-->
<aura:component >
    <aura:attribute name="ATrue" type="boolean" default="true"/>
    <aura:attribute name="BFalse" type="boolean" default="false"/>
    <aura:attribute name="CTrue" type="boolean" default="true"/>
    <aura:attribute name="DFalse" type="boolean" default="false"/>
    
    <!--aura:if Sample -->
    <aura:if  isTrue="{!and(v.ATrue, v.CTrue)}" >
        <div style="padding:15px; background-color:LightBlue">
            And => This div show because both A and C attributes are true
        </div>
    </aura:if>
    <!--aura:if with aura:set sample-->
    <aura:if  isTrue="{!or(v.DFalse, v.BFalse)}">
        <div style="padding:15px;">
            Or => This div show because one attribute is true
        </div>
        <aura:set attribute="else">
            <div style="padding:15px;">
                Or => This div show because both attributes are false
            </div>
        </aura:set>
    </aura:if>
    <!--aura:if with nested and condition-->
    <aura:if  isTrue="{!or(and(v.ATrue, v.CTrue), v.BFalse)}" >
        <div style="padding:15px; background-color:LightGreen  ">
            Nested condition div show because in statment one of or condition returns true
        </div>
    </aura:if>
</aura:component>

Lightning App:

<!--TestApp-->
<aura:application>
    <c:TestComponent/>
</aura:application>

Output: