Hello, Friends today we will learn to add new rows /remove and perform DML operations. Add/Remove Rows in Data table in LWC Salesforce
Key Highlights
- We create a LWC quick action on Account record
- Popup will display any existing contacts
- Ability to add new rows to add new contacts
- Ability to update existing contacts
- Ability to delete contacts (A delete icon in front of every row)
- Clicking on save button will invoke an apex method to Create, Update and Delete contacts in a single operation.
- Display spinner on save operation
- Error handling
We will add/remove Rows in the Data table in LWC Salesforce in this video.
Check out this: Expand/Collapse Section in LWC
Lets start our code
AccountContactTable.Html :
<template>
<lightning-quick-action-panel header="Manage Contacts">
<div class="slds-p-around_none slds-m-top_x-small slds-m-bottom_medium slds-m-horizontal_none modalBodyy">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading" size="medium" class="spinnerClass"></lightning-spinner>
</template>
<lightning-card>
<lightning-button label="Add Row" slot="actions" icon-name="utility:add" onclick={addRow}></lightning-button>
<div class="slds-m-around_medium">
<table class="slds-table slds-table_cell-buffer slds-table_bordered" aria-labelledby="element-with-table-label other-element-with-table-label">
<thead>
<tr class="slds-line-height_reset">
<th class="" scope="col">
<div class="slds-truncate" title="First Name">First Name</div>
</th>
<th class="" scope="col">
<div class="slds-truncate" title="Last Name">Last Name</div>
</th>
<th class="" scope="col">
<div class="slds-truncate" title="Email">Email</div>
</th>
<th class="" scope="col">
<div class="slds-truncate" title="Stage">Action</div>
</th>
</tr>
</thead>
<tbody>
<template for:each={records} for:item="obj">
<tr class="inputRows" key={obj.Id}>
<th data-label="Opportunity Name" scope="row">
<lightning-input type="text" class="fields" variant="label-hidden" label="First Name" name="FirstName" value={obj.FirstName} data-id={obj.Id} onchange={updateValues}></lightning-input>
</th>
<td data-label="Account Name">
<lightning-input type="text" class="fields" variant="label-hidden" label="Last Name" name="LastName" value={obj.LastName} data-id={obj.Id} onchange={updateValues}></lightning-input>
</td>
<td data-label="Close Date">
<lightning-input type="text" class="fields" variant="label-hidden" label="Email" name="Email" value={obj.Email} data-id={obj.Id} onchange={updateValues}></lightning-input>
</td>
<td data-label="Prospecting">
<lightning-button-icon icon-name="action:delete" alternative-text="Delete" title="Delete" data-id={obj.Id} onclick={handleDeleteAction}></lightning-button-icon>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</lightning-card>
</div>
<div slot="footer">
<lightning-button variant="neutral" label="Cancel" onclick={closeAction}></lightning-button>
<lightning-button variant="brand" label="Save" onclick={handleSaveAction} disabled={isDisable}></lightning-button>
</div>
</lightning-quick-action-panel>
</template>
AccountContactTable.Js :
import { LightningElement, api, track, wire } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
import { ShowToastEvent } from 'lightning/platformShowToastEvent'
import fetchContacts from '@salesforce/apex/ContactController.fetchContacts';
import dmlOnContacts from '@salesforce/apex/ContactController.dmlOnContacts';
import { refreshApex } from '@salesforce/apex';
export default class AccountContactTable extends LightningElement {
@api recordId;
@track isLoading = true;
@track records;
wiredRecords;
error;
@track deleteConatctIds = '';
//to close quick action
closeAction(){
this.dispatchEvent(new CloseActionScreenEvent());
}
//to add row
addRow() {
let randomId = Math.random() * 16;
let myNewElement = {Email: "", FirstName: "", Id: randomId, LastName: "", AccountId: this.recordId};
this.records = [...this.records, myNewElement];
}
get isDisable(){
return (this.isLoading || (this.wiredRecords.data.length == 0 && this.records.length == 0));
}
//show/hide spinner
handleIsLoading(isLoading) {
this.isLoading = isLoading;
}
//update table row values in list
updateValues(event){
var foundelement = this.records.find(ele => ele.Id == event.target.dataset.id);
if(event.target.name === 'FirstName'){
foundelement.FirstName = event.target.value;
} else if(event.target.name === 'LastName'){
foundelement.LastName = event.target.value;
} else if(event.target.name === 'Email'){
foundelement.Email = event.target.value;
}
}
//handle save and process dml
handleSaveAction(){
if(this.handleCheckValidation()) {
this.handleIsLoading(true);
if(this.deleteConatctIds !== ''){
this.deleteConatctIds = this.deleteConatctIds.substring(1);
}
this.records.forEach(res =>{
if(!isNaN(res.Id)){
res.Id = null;
}
});
dmlOnContacts({data: this.records, removeContactIds : this.deleteConatctIds})
.then( result => {
this.handleIsLoading(false);
refreshApex(this.wiredRecords);
this.updateRecordView(this.recordId);
this.closeAction();
this.showToast('Success', result, 'Success', 'dismissable');
}).catch( error => {
this.handleIsLoading(false);
console.log(error);
this.showToast('Error updating or refreshing records', error.body.message, 'Error', 'dismissable');
});
}
}
//remove records from table
handleDeleteAction(event){
if(isNaN(event.target.dataset.id)){
this.deleteConatctIds = this.deleteConatctIds + ',' + event.target.dataset.id;
}
this.records.splice(this.records.findIndex(row => row.Id === event.target.dataset.id), 1);
}
//fetch account contact records
@wire(fetchContacts, {recordId : '$recordId'})
wiredContact(result) {
this.wiredRecords = result; // track the provisioned value
const { data, error } = result;
if(data) {
this.records = JSON.parse(JSON.stringify(data));
this.error = undefined;
this.handleIsLoading(false);
} else if(error) {
this.error = error;
this.records = undefined;
this.handleIsLoading(false);
}
}
showToast(title, message, variant, mode) {
const event = new ShowToastEvent({
title: title,
message: message,
variant: variant,
mode: mode
});
this.dispatchEvent(event);
}
updateRecordView() {
setTimeout(() => {
eval("$A.get('e.force:refreshView').fire();");
}, 3000);
}
//added this method for validate input if any required
handleCheckValidation() {
let isValid = true;
let inputFields = this.template.querySelectorAll('.fieldvalidate');
inputFields.forEach(inputField => {
if(!inputField.checkValidity()) {
inputField.reportValidity();
isValid = false;
}
});
return isValid;
}
}
AccountContactTable.css :
.modalBodyy {
position: relative;
}
accountContactTable.js-meta.xml :
<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordAction</target>
<target>lightning__RecordPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordAction">
<actionType>ScreenAction</actionType>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
ContactController.cls :
public class ContactController {
@AuraEnabled( cacheable = true )
public static List<Contact> fetchContacts(String recordId) {
return [SELECT Id, AccountId, FirstName, LastName, Email FROM Contact WHERE AccountId =:recordId LIMIT 100];
}
@AuraEnabled
public static string dmlOnContacts(Object data, String removeContactIds) {
List<Contact> updateContact = (List<Contact>) JSON.deserialize(JSON.serialize(data), List<Contact>.class);
List<Contact> deleteContact = new List<Contact>();
if(String.isNotBlank(removeContactIds)){
List<Id> contactIds = removeContactIds.split(',');
deleteContact = [SELECT Id FROM Contact WHERE Id IN :contactIds];
}
try {
if(updateContact != null && !updateContact.isEmpty()){
upsert updateContact;
}
if(deleteContact != null && !deleteContact.isEmpty()){
delete deleteContact;
}
return 'Success: Contact(s) upsert/delete successfully';
}
catch (Exception e) {
String errorMsg = 'The following exception has occurred: ' + e.getMessage();
throw new AuraHandledException(ErrorMsg);
}
// return '';
}
}
What’s your Reaction?
+1
9
+1
1
+1
1
+1
1
+1
2
+1
2
8 comments
Hello, i create a table with custom field and it´s not working, dont update fields. Can u help me?
which object you are using of update.
I want to customize this I dont want to display the default 3 rows how can i remove them?
Three rows showing according contact records. IN my case there are 3 contact record on account level.
if a user click on Add row button but do not fill any data on cells and click on save button. so it will be problematic i think. can you please share with required fields on cells. means if we add row so we set a validation that “please fill the fields”. can you please share the updated code in this.
thanks..!!
Hi @Harsh, Sure I will fill this missing gap in my code.
Thanks for your suggestion.
Hi @Harsh, I updated the code for validation.
Thanks, Rijwan
After checking in my org i will let you know..
thanks for your precious time!!