Apex Class:
public class DynamicRowsController {
@AuraEnabled
public static String updateAccts( List < Account > listAccts ) {
try {
insert listAccts;
return 'Successful';
} catch ( Exception e ) {
throw new AuraHandledException( e.getMessage() );
}
}
}
Lightning Web Component:
HTML:
<template>
<lightning-card>
<table class="slds-table">
<tr>
<th>Name</th>
<th>Industry</th>
<th>Active Date</th>
<th>Is Active?</th>
<th>Type</th>
<th>Description</th>
<th>Action</th>
</tr>
<template iterator:it={accounts}>
<tr key={it.value.Name}>
<td>
<lightning-input
type="text"
label="Account Name"
value={it.value.Name}
data-record-id={it.index}
onchange={handleChange}
variant="label-hidden">
</lightning-input>
</td>
<td>
<lightning-combobox
key={it.value.Name}
value={it.value.Industry}
options={industryOptions}
data-record-id={it.index}
onchange={handleChange}
label="Industry"
variant="label-hidden">
</lightning-combobox>
</td>
<td><lightning-input
type="date"
label="Active Date"
value={it.value.Active_Date__c}
data-record-id={it.index}
onchange={handleChange}
variant="label-hidden">
</lightning-input>
</td>
<td><lightning-input
type="checkbox"
label="Is Active?"
value={it.value.Is_Active__c}
data-record-id={it.index}
onchange={handleChange}
variant="label-hidden">
</lightning-input>
</td>
<td>
<lightning-combobox
key={it.value.Name}
value={it.value.Type}
data-record-id={it.index}
options={typeOptions}
onchange={handleChange}
label="Type">
</lightning-combobox>
</td>
<td><lightning-input
type="text"
label="Description"
value={it.value.Description}
data-record-id={it.index}
onchange={handleChange}>
</lightning-input>
</td>
<td><lightning-button
variant="base"
label="Delete"
onclick={deleteRow}
data-record-id={it.index}>
</lightning-button>
</td>
</tr>
</template>
<tr>
<td><lightning-button label="Add Row" onclick={addRow}></lightning-button></td>
</tr>
</table>
<p slot="footer">
<lightning-button variant="brand" label="Save Records" onclick={saveAccounts}></lightning-button>
</p>
</lightning-card>
</template>
JavaScript:
import { LightningElement, wire } from 'lwc';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
import TYPE_FIELD from '@salesforce/schema/Account.Type';
import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import updateAccts from '@salesforce/apex/DynamicRowsController.updateAccts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class DynamicRows extends LightningElement {
accounts;
error;
typeOptions;
industryOptions;
connectedCallback() {
this.accounts = [ {
'Name' : null,
'Industry' : null,
'Active_Date__c' : null,
'Is_Active__c' : false,
'Type' : null,
'Description' : null
} ];
}
@wire( getObjectInfo, { objectApiName: ACCOUNT_OBJECT } )
objectInfo;
@wire( getPicklistValues, { recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: INDUSTRY_FIELD } )
getIndustryValues( { error, data } ) {
if ( data ) {
this.industryOptions = data.values.map( objPL => {
return {
label: `${objPL.label}`,
value: `${objPL.value}`
};
});
} else if ( error ) {
console.error( JSON.stringify( error ) );
}
}
@wire( getPicklistValues, { recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: TYPE_FIELD } )
getTypeValues( { error, data } ) {
if ( data ) {
this.typeOptions = data.values.map( objPL => {
return {
label: `${objPL.label}`,
value: `${objPL.value}`
};
});
} else if ( error ) {
console.error( JSON.stringify( error ) );
}
}
addRow() {
let newEntry = {
'Name' : null,
'Industry' : null,
'Active_Date__c' : null,
'Is_Active__c' : false,
'Type' : null,
'Description' : null
};
if ( this.accounts ) {
this.accounts = [ ...this.accounts, newEntry ];
} else {
this.accounts = [ newEntry ];
}
}
deleteRow( event ) {
let strIndex = event.target.dataset.recordId;
let tempAccounts = this.accounts;
tempAccounts.splice( strIndex, 1 );
console.log( 'Temp Accounts are ' + JSON.stringify( tempAccounts ) );
this.accounts = JSON.parse( JSON.stringify( tempAccounts ) );
}
handleChange( event ) {
let recs = this.accounts;
let value = event.target.value;
let label = event.target.label;
let name;
switch( label ) {
case 'Account Name':
name = 'Name';
break;
case 'Industry':
name = 'Industry';
break;
case 'Active Date':
name = 'Active_Date__c';
break;
case 'Is Active?':
name = 'Is_Active__c';
value = event.target.checked;
break;
case 'Type':
name = 'Type';
break;
case 'Description':
name = 'Description';
break;
}
console.log( label + ' - ' + value );
let strIndex = event.target.dataset.recordId;
console.log( 'Index is ' + strIndex );
let rec = recs[ strIndex ];
rec[ name ] = value;
recs[ strIndex ] = rec;
console.log( 'Temp Accounts are ' + JSON.stringify( recs ) );
this.accounts = JSON.parse( JSON.stringify( this.accounts ) );
}
saveAccounts() {
console.log( 'Temp Accounts are ' + JSON.stringify( this.accounts ) );
updateAccts( { listAccts : this.accounts } )
.then( result => {
console.log( 'Result ' + JSON.stringify( result ) );
let message;
let variant;
if ( result === 'Successful' ) {
message = 'Successfully Processed!';
variant = 'success';
} else {
message = 'Some error occured. Please reach out to your Salesforce Admin for help!';
variant = 'error';
}
const toastEvent = new ShowToastEvent( {
title: 'Account creation',
message: message,
variant: variant
} );
this.dispatchEvent( toastEvent );
} )
.catch( error => {
console.log( 'Error ' + JSON.stringify( error ) );
} );
}
}
JS-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>52.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>
Output: