Dynamic Column Validation in LWC Datatables with ES6 Methods

Use case:

To display a list of records in a datatable with a percentage as one of the columns. The requirement, however, is specific – the sum of all rows in the percentage column must equal 100%, with users allowed to perform inline editing.


While the use case sounds simple, implementing it in LWC presents unique challenges due to how datatable manages data. The datatable component uses two key attributes for handling its content:

  • data: Holds the initial list of records to display.
  • draftValues: Stores only the values that have been edited, creating a separate data array that needs to be reconciled with the original data.

We all know there are various ways to address this issue, but I prefer to opt out by utilizing ES6 methods. Let’s divide the solution into three sections:

  1. Setting up the datatable with percentage values.
  2. Handling inline editing using draftValues.
  3. Calculating the percentage sum in real-time, ensuring it always equals 100%.

The key to the solution lies in ES6 array methods like map and reduce, which allow us to write clean, concise, and efficient code to handle the array manipulations required.

Step-by-Step Implementation

Step 1: Initial Setup of the Datatable Component

First, we initialize the component with data holding our records and set up the columns with inline editing enabled for the percentage field.

import { LightningElement } from 'lwc';

export default class PercentageDatatable extends LightningElement {
    data = [
        { id: '1', name: 'Record A', percentage: 20 },
        { id: '2', name: 'Record B', percentage: 30 },
        { id: '3', name: 'Record C', percentage: 50 }
    ];

    columns = [
        { label: 'Name', fieldName: 'name', type: 'text' },
        {
            label: 'Percentage',
            fieldName: 'percentage',
            type: 'number',
            editable: true
        }
    ];

    draftValues = [];
}

In this setup:

  • data contains the original records.
  • columns defines the structure, with percentage set as an editable field.
  • draftValues will store the inline-edited values.

Step 2: Handling Inline Edit Changes

We add an event handler to track the inline changes. Whenever a user edits a percentage value, we capture the new values in draftValues.

handleSave(event) {
    // Capture edited values
    this.draftValues = event.detail.draftValues;
    
    // Merge draftValues with the original data to get updated records
    const updatedData = this.data.map(record => {
        const draft = this.draftValues.find(d => d.id === record.id);
        return draft ? { ...record, ...draft } : record;
    });

    // Calculate the total percentage <3 
    const totalPercentage = updatedData.reduce((sum, record) => sum + parseFloat(record.percentage || 0), 0);

    if (totalPercentage === 100) {
        // Success case: total percentage equals 100
        this.data = updatedData;
        this.draftValues = [];
        // You may also want to show a success message to the user
    } else {
        // Error case: percentages do not add up to 100
        this.showErrorMessage('The total percentage must equal 100%.');
    }
}

In this code:

  • We use a map to combine draftValues with the original data, ensuring we consider both the original and edited values.
  • We use reduce to calculate the total percentage by summing up the percentage values across all records.

Step 3: Displaying Error Messages

When the percentage sum does not equal 100, we provide feedback to the user to correct the values.

// throw an error using toast event
showErrorMessage(message) {
    const errorEvent = new ShowToastEvent({
        title: 'Error',
        message: message,
        variant: 'error',
    });
    this.dispatchEvent(errorEvent);
}

Using ShowToastEvent, we display an error message if the total percentage deviates from 100%, prompting the user to adjust their entries accordingly.

Implementing the 100% validation for a percentage column in Salesforce LWC datatable with inline editing required a strategic approach to handling data and draftValues arrays. By leveraging ES6 methods like map and reduce, we efficiently merged these arrays and calculated the total percentage in just a few lines of readable and maintainable code.


Here's the complete JavaScript code for reference:

import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class percentageDatatable extends LightningElement {
    data = [
        { id: '1', name: 'Record A', percentage: 20 },
        { id: '2', name: 'Record B', percentage: 30 },
        { id: '3', name: 'Record C', percentage: 50 }
    ];

    columns = [
        { label: 'Name', fieldName: 'name', type: 'text' },
        { label: 'Percentage', fieldName: 'percentage', type: 'number', editable: true }
    ];

    draftValues = [];

    handleSave(event) {
        this.draftValues = event.detail.draftValues;
        
        const updatedData = this.data.map(record => {
            const draft = this.draftValues.find(d => d.id === record.id);
            return draft ? { ...record, ...draft } : record;
        });

        const totalPercentage = updatedData.reduce((sum, record) => sum + parseFloat(record.percentage || 0), 0);

        if (totalPercentage === 100) {
            this.data = updatedData;
            this.draftValues = [];
        } else {
            this.showErrorMessage('The total percentage must equal 100%.');
        }
    }

    showErrorMessage(message) {
        const errorEvent = new ShowToastEvent({
            title: 'Error',
            message: message,
            variant: 'error',
        });
        this.dispatchEvent(errorEvent);
    }
}

percentageDatatable.js