+ Start a Discussion
Siva SakthiSiva Sakthi 

Dynamically add/remove rows for a table in LWC

Hi, I am new to LWC ​​​​​​, trying dynamically add/remove rows for a table scenario. I can able to add the row by click on the + button. After added that rows I want to enter some values in text boxes and save into account object by click on save buttton. Also do the delete action as well to delete the specific row by click on delete button. 

I am facing issue with save & delete records. How I can solve this? Can anyone give me some guidence to solve.

AddDeleteRow
dynamicAddRow.html

<template>
                  
    <div class="slds-m-around--xx-large">
        <div class="slds-float_right slds-p-bottom_small">
            <h1 class="slds-page-header__title">Add Row
                <lightning-button-icon icon-name="utility:add"  size="large" variant="bare" alternative-text="Add" onclick={addRow}> </lightning-button-icon>
            </h1>
        </div>
        <div class="container-fluid">        
            <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>      
                    
                    <template for:each={accountList} for:item="acc" for:index="index">
                        <tr key={acc.Id}> 
                            <td>{index}</td>                                                  
                            <td>
                                <lightning-input label="Name" value={acc.Name} onchange={handleNameChange}></lightning-input>                               
                            </td>
                            <td>
                                <lightning-input label="Account Number" value={acc.AccountNumber} onchange={handleAccountNumberChange}></lightning-input>                        
                            </td>
                            <td>
                                <lightning-input label="Phone" value={acc.Phone} onchange={handlePhoneChange}></lightning-input>
                            </td>
                            <td>
                                <a onclick={removeRow}> 
                                    <lightning-icon icon-name="utility:delete" size="small" style="margin-top: -4px; margin-right: 0px;" ></lightning-icon>
                                    <span class="slds-assistive-text">Delete</span>
                                </a>
                            </td> 
                        </tr>
                    </template>
                     
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">                
                <lightning-button name="Save" label="Save" onclick={saveRecord} ></lightning-button>
            </div>
        </div>
    </div>

</template>
 
dynamicAddRow.js

import { LightningElement, track,api } from 'lwc';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNTNUMBER_FIELD from '@salesforce/schema/Account.AccountNumber';
import PHONE_FIELD from '@salesforce/schema/Account.Phone';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class CreateDynamicRecord extends LightningElement {
    @track accountList = []; 
    @track index = 0;
    @api recordId;
    @track name = NAME_FIELD;
    @track industry = ACCOUNTNUMBER_FIELD;
    @track phone = PHONE_FIELD;

    @api record = {
        firstName : '',
        lastName : '',
        Email : '',
        Phone : '',
        Title : ''
    }

    addRow(){

        this.index++;
   
        this.accountList.push ({
            sobjectType: 'Account',
            Name: '',
            AccountNumber : '',
            Phone: ''
        });

        console.log('Enter ',this.accountList);
        
       // this.accountList.push(this.record);
        //console.log(' After adding Record List ', this.accountList);
    }
    
    removeRow(){

        var index = this.index;
      
        if(this.accountList.length>1)
           this.accountList.splice(index, 1);

        //this.dispatchEvent(new CustomEvent('deleterow', {detail: this.index}));
        //console.log(' After adding Record List ', this.dispatchEvent);
    } 

    acc = {
        Name : this.name,
        AccountNumber : this.accNumber,
        Phone : this.phone
    }

    handleNameChange(event) {
        this.acc.Name = event.target.value;
        console.log("name", this.acc.Name);
    }
    
    handleAccountNumberChange(event) {
        this.acc.AccountNumber = event.target.value;
        console.log("AccountNumber", this.acc.AccountNumber);
    }
    
    handlePhoneChange(event) {
        this.acc.Phone = event.target.value;
        console.log("Phone", this.acc.Phone);
    }
    
    saveRecord(){        
        saveAccounts(this.acc.accountList)
            .then(result => {
                this.message = result;
                this.error = undefined;
                if(this.message !== undefined) {
                    this.acc.Name = '';
                    this.acc.AccountNumber = '';
                    this.acc.Phone = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Account created successfully',
                            variant: 'success',
                        }),
                    );
                }
                
                console.log(JSON.stringify(result));
                console.log("result", this.message);
                /*console.log(' After adding Record List ', result);
                this.accountList = result;
                console.log(' After adding Record List ', this.accountList);*/
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
      
}
 
AccountController.apex  

public with sharing class AccountController { 
 
    @AuraEnabled( cacheable = true ) 
    public static List< Account > getAccounts() { 
      
        return [ SELECT Id, Name, Industry FROM Account LIMIT 10 ]; 
         
    } 
     
    @AuraEnabled( cacheable = true )
    public static void saveAccounts(List<Account> accList){
        Insert accList;
        /*if(accList.size()>0 && accList != null){
            insert accList;
        }*/
    } 
}
Thanks 
Siva
 
Best Answer chosen by Siva Sakthi
Danish HodaDanish Hoda
Hi Siva,
Please refer below code:
 
html:

<template>
    <div if:true={isLoaded} class="cstm-spinner">
		<lightning-spinner alternative-text="Loading..."></lightning-spinner>
	</div>
                  
    <div class="slds-m-around--xx-large container-fluid">
        <div class="slds-float_right slds-p-bottom_small">
            <h1 class="slds-page-header__title">Add Row
                <lightning-button-icon icon-name="utility:add"  size="large" variant="bare" alternative-text="Add" onclick={addRow}> </lightning-button-icon>
            </h1>
        </div>
        <div class="container-fluid">        
            <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>      
                    
                    <template for:each={accountList} for:item="acc" for:index="indx">
                        <tr key={acc.key} id={acc.key}> 
                            <td>{indx}</td>                                                  
                            <td>
                                <lightning-input data-id={indx} label="Name" value={acc.Name} onchange={handleNameChange}></lightning-input>                               
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Account Number" value={acc.AccountNumber} onchange={handleAccountNumberChange}></lightning-input>                        
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Phone" value={acc.Phone} onchange={handlePhoneChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-button-icon icon-name="utility:delete"
																		  data-id={indx}       
																		  alternative-text="Delete"     
																		  class="slds-m-left_xx-small"
																		  onclick={removeRow} 
																		  title="Delete"></lightning-button-icon>
                            </td>
                        </tr>
                    </template>
                     
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">                
                <lightning-button name="Save" label="Save" onclick={saveRecord} ></lightning-button>
            </div>
        </div>
    </div>

</template>
 
js :

import { LightningElement, track,api } from 'lwc';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNTNUMBER_FIELD from '@salesforce/schema/Account.AccountNumber';
import PHONE_FIELD from '@salesforce/schema/Account.Phone';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class CreateDynamicRecord extends LightningElement {
    @track accountList = []; 
    @track index = 0;
    @api recordId;
    @track name = NAME_FIELD;
    @track accNumber = ACCOUNTNUMBER_FIELD;
    @track phone = PHONE_FIELD;
    isLoaded = false;

    @api record = {
        firstName : '',
        lastName : '',
        Email : '',
        Phone : '',
        Title : ''
    }

    acc = {
        Name : this.name,
        AccountNumber : this.accNumber,
        Phone : this.phone ? this.phone : "",
        key : ''
    }

    addRow(){

        this.index++;
        //var i = JSON.parse(JSON.stringify(this.index));
        var i = this.index;
   
        /*this.accountList.push ({
            sobjectType: 'Account',
            Name: '',
            AccountNumber : '',
            Phone: '',
            key : i
        });*/
        this.acc.key = i;
        this.accountList.push(JSON.parse(JSON.stringify(this.acc)));

        console.log('Enter ',this.accountList);
        
       // this.accountList.push(this.record);
        //console.log(' After adding Record List ', this.accountList);
    }
    
    removeRow(event){
        this.isLoaded = true;
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        if(this.accountList.length>1){
            this.accountList.splice(key, 1);
            this.index--;
            this.isLoaded = false;
        }else if(this.accountList.length == 1){
            this.accountList = [];
            this.index = 0;
            this.isLoaded = false;
        }

        //this.dispatchEvent(new CustomEvent('deleterow', {detail: this.index}));
        //console.log(' After adding Record List ', this.dispatchEvent);
    } 

    

    handleNameChange(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Name = event.target.value;
        //this.acc.Name = event.target.value;
        //console.log("name", this.acc.Name);
    }
    
    handleAccountNumberChange(event) {
        /*this.acc.AccountNumber = event.target.value;
        console.log("AccountNumber", this.acc.AccountNumber);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].AccountNumber = event.target.value;
    }
    
    handlePhoneChange(event) {
        /*this.acc.Phone = event.target.value;
        console.log("Phone", this.acc.Phone);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Phone = event.target.value;
    }
    
    saveRecord(){        
        saveAccounts({accList : this.accountList})
            .then(result => {
                this.message = result;
                this.error = undefined;
                if(this.message !== undefined) {
                    this.acc.Name = '';
                    this.acc.AccountNumber = '';
                    this.acc.Phone = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Account created successfully',
                            variant: 'success',
                        }),
                    );
                }
                
                console.log(JSON.stringify(result));
                console.log("result", this.message);
                /*console.log(' After adding Record List ', result);
                this.accountList = result;
                console.log(' After adding Record List ', this.accountList);*/
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
      
}
 
AccountController.apex  

public with sharing class AccountController { 
 
    @AuraEnabled( cacheable = true ) 
    public static List< Account > getAccounts() { 
      
        return [ SELECT Id, Name, Industry FROM Account LIMIT 10 ]; 
         
    } 
     
//( cacheable = true ) doesn't support DML operations
    @AuraEnabled
    public static void saveAccounts(List<Account> accList){
        Insert accList;
        /*if(accList.size()>0 && accList != null){
            insert accList;
        }*/
    } 
}

 

All Answers

Danish HodaDanish Hoda
Hi Siva,
Please refer below code:
 
html:

<template>
    <div if:true={isLoaded} class="cstm-spinner">
		<lightning-spinner alternative-text="Loading..."></lightning-spinner>
	</div>
                  
    <div class="slds-m-around--xx-large container-fluid">
        <div class="slds-float_right slds-p-bottom_small">
            <h1 class="slds-page-header__title">Add Row
                <lightning-button-icon icon-name="utility:add"  size="large" variant="bare" alternative-text="Add" onclick={addRow}> </lightning-button-icon>
            </h1>
        </div>
        <div class="container-fluid">        
            <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>      
                    
                    <template for:each={accountList} for:item="acc" for:index="indx">
                        <tr key={acc.key} id={acc.key}> 
                            <td>{indx}</td>                                                  
                            <td>
                                <lightning-input data-id={indx} label="Name" value={acc.Name} onchange={handleNameChange}></lightning-input>                               
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Account Number" value={acc.AccountNumber} onchange={handleAccountNumberChange}></lightning-input>                        
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Phone" value={acc.Phone} onchange={handlePhoneChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-button-icon icon-name="utility:delete"
																		  data-id={indx}       
																		  alternative-text="Delete"     
																		  class="slds-m-left_xx-small"
																		  onclick={removeRow} 
																		  title="Delete"></lightning-button-icon>
                            </td>
                        </tr>
                    </template>
                     
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">                
                <lightning-button name="Save" label="Save" onclick={saveRecord} ></lightning-button>
            </div>
        </div>
    </div>

</template>
 
js :

import { LightningElement, track,api } from 'lwc';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNTNUMBER_FIELD from '@salesforce/schema/Account.AccountNumber';
import PHONE_FIELD from '@salesforce/schema/Account.Phone';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class CreateDynamicRecord extends LightningElement {
    @track accountList = []; 
    @track index = 0;
    @api recordId;
    @track name = NAME_FIELD;
    @track accNumber = ACCOUNTNUMBER_FIELD;
    @track phone = PHONE_FIELD;
    isLoaded = false;

    @api record = {
        firstName : '',
        lastName : '',
        Email : '',
        Phone : '',
        Title : ''
    }

    acc = {
        Name : this.name,
        AccountNumber : this.accNumber,
        Phone : this.phone ? this.phone : "",
        key : ''
    }

    addRow(){

        this.index++;
        //var i = JSON.parse(JSON.stringify(this.index));
        var i = this.index;
   
        /*this.accountList.push ({
            sobjectType: 'Account',
            Name: '',
            AccountNumber : '',
            Phone: '',
            key : i
        });*/
        this.acc.key = i;
        this.accountList.push(JSON.parse(JSON.stringify(this.acc)));

        console.log('Enter ',this.accountList);
        
       // this.accountList.push(this.record);
        //console.log(' After adding Record List ', this.accountList);
    }
    
    removeRow(event){
        this.isLoaded = true;
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        if(this.accountList.length>1){
            this.accountList.splice(key, 1);
            this.index--;
            this.isLoaded = false;
        }else if(this.accountList.length == 1){
            this.accountList = [];
            this.index = 0;
            this.isLoaded = false;
        }

        //this.dispatchEvent(new CustomEvent('deleterow', {detail: this.index}));
        //console.log(' After adding Record List ', this.dispatchEvent);
    } 

    

    handleNameChange(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Name = event.target.value;
        //this.acc.Name = event.target.value;
        //console.log("name", this.acc.Name);
    }
    
    handleAccountNumberChange(event) {
        /*this.acc.AccountNumber = event.target.value;
        console.log("AccountNumber", this.acc.AccountNumber);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].AccountNumber = event.target.value;
    }
    
    handlePhoneChange(event) {
        /*this.acc.Phone = event.target.value;
        console.log("Phone", this.acc.Phone);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Phone = event.target.value;
    }
    
    saveRecord(){        
        saveAccounts({accList : this.accountList})
            .then(result => {
                this.message = result;
                this.error = undefined;
                if(this.message !== undefined) {
                    this.acc.Name = '';
                    this.acc.AccountNumber = '';
                    this.acc.Phone = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Account created successfully',
                            variant: 'success',
                        }),
                    );
                }
                
                console.log(JSON.stringify(result));
                console.log("result", this.message);
                /*console.log(' After adding Record List ', result);
                this.accountList = result;
                console.log(' After adding Record List ', this.accountList);*/
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
      
}
 
AccountController.apex  

public with sharing class AccountController { 
 
    @AuraEnabled( cacheable = true ) 
    public static List< Account > getAccounts() { 
      
        return [ SELECT Id, Name, Industry FROM Account LIMIT 10 ]; 
         
    } 
     
//( cacheable = true ) doesn't support DML operations
    @AuraEnabled
    public static void saveAccounts(List<Account> accList){
        Insert accList;
        /*if(accList.size()>0 && accList != null){
            insert accList;
        }*/
    } 
}

 
This was selected as the best answer
Siva SakthiSiva Sakthi
Hi Danish Hoda,   
     Thank you for your reply. Its working fine. I want to remove the header labels from each row. Only one time show the header label on top.
Like contact is child, I am using this same component to create child contact records under the related Account. One Account have multiple contact records. How to modify this component. I will try to use this component in parent related list. Please give me some guidence to solve. 

Thanks
Siva
Danish HodaDanish Hoda
Hi Siva,
  •  I want to remove the header labels from each row. Only one time show the header label on top. - Please remove label attribute from the lightning-inputs.
  • Per your table here, you are creating new records on Account, may I know how you are trying to achieve creating related contacts at the same time.
PS : Please ask a new question and let us discuss there as we should mark this one as Closed and Done. :)
Maharajan CMaharajan C
Sample Code :  Dynamic Add/Remove LWC (https://developer.salesforce.com/forums/ForumsMain?id=9062I000000Xw8WQAS)

Thanks,
Maharajan.C
Ayush Kumar SinghAyush Kumar Singh
Hey, I am adding a new row, all the input fields are empty after adding the new row, even though I have given the input
​​​​​​​ in the first row I have given the input but when I hit add row button , row is added but all  the rows input field become empty