I have a custom lwc that displays a lightning-datatable
and I want some rows to have its checkbox disabled.
Is it possible to set this dynamically? Or do I have to manage this via onrowselection
function?
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It only takes a minute to sign up.
Sign up to join this communityThis is possible. With web components, we have the ability to extend existing classes if those classes have the export keyword.
Using browser developer tools, the lightning-datatable component loads a JS file called datatable.js. In this file there is class LightningDatatable( the default export) which has a function updateRowsState() and updateColumns() . We need to override these function in a new custom component by removing the this. notation on the function updateRowsAndCellIndexes which is called from updateColumns() and updateRowsState() functions which we are overriding.
So create a new LWC called "customDataTable" which extends the out of the box lightning-datatable.
The goal here to disable a row is rows array in the "state" variable. Each element in rows is of type checkbox and has an attribute "isDisabled". You need to write a logic to set isDisable = true based upon values from the state.data array (this is the data that will get rendered on the table). This is the function "isDataRowDisabled".
Now also copy the function updateRowsAndCellIndexes and modify it as below.
Note: this does solve a specific purpose. But there are risks if Salesforce change the code for the base components. Also you will need to import a lot functions from the base javascript file for this change to work else you will get a compiling error.
Onrowselected is not a good user experience.
Hope this helps.
import LightningDataTable from 'lightning/datatable';
export default class customDataTable extends LightningDataTable {
constructor() {
super();
}
updateRowsAndCellIndexesNew() {
const {
state,
privateTypes: types
} = this;
const {
keyField
} = state;
const data = getData(state);
const columns = getColumns(state);
const {
computeUniqueRowKey
} = createRowKeysGenerator(keyField);
const scopeCol = columns.find(colData => types.isValidType(colData.type) && colData.isScopeCol); // initializing indexes
state.indexes = {};
state.rows = data.reduce((prev, rowData, rowIndex) => {
const row = {
key: computeUniqueRowKey(rowData),
// attaching unique key to the row
cells: []
};
const rowErrors = getRowError(state, row.key);
state.indexes[row.key] = {
rowIndex
};
row.inputType = getRowSelectionInputType(state);
row.isSelected = isSelectedRow(state, row.key);
row.ariaSelected = row.isSelected ? 'true' : false;
//row.isDisabled = isDisabledRow(state, row.key);
row.isDisabled = this.isDataRowDisabled(state, row.key);
row.classnames = resolveRowClassNames(row);
Object.assign(row, getRowStateForTree(rowData, state));
row.tabIndex = -1;
columns.reduce((currentRow, colData, colIndex) => {
const {
fieldName
} = colData;
const colKeyValue = generateColKeyValue(colData, colIndex);
const dirtyValue = getDirtyValue(state, row.key, colKeyValue);
const computedCellValue = dirtyValue !== undefined ? dirtyValue : rowData[fieldName];
const colType = types.getType(colData.type); // cell object creation
const cell = {
columnType: colData.type,
columnSubType: colData.typeAttributes ? colData.typeAttributes.subType : undefined,
dataLabel: colData.label,
value: computedCellValue,
// value based on the fieldName
rowKeyValue: row.key,
// unique row key value
colKeyValue,
// unique column key value
tabIndex: -1,
// tabindex
isCheckbox: colData.type === 'SELECTABLE_CHECKBOX',
class: computeCellClassNames(colData, rowErrors, dirtyValue),
hasError: rowErrors.fieldNames && rowErrors.fieldNames.includes(colData.fieldName),
isDataType: types.isValidType(colData.type) && !colData.isScopeCol,
isDataTypeScope: types.isValidType(colData.type) && colData.isScopeCol,
wrapText: state.wrapText[colKeyValue],
// wrapText state
style: colType && colType.type === 'custom' ? 'padding: 0px;' : undefined
};
if (isCustomerColumn(colData)) {
Object.assign(cell, computeCellTypeAttributes(rowData, colData, types), computeCellAttributes(rowData, colData), computeCellEditable(colData));
if (isTreeType(colData.type)) {
Object.assign(cell, computeCellStateTypeAttributes(row));
}
} else if (isRowNumberColumn(colData)) {
const scopeColValue = rowData[scopeCol.fieldName];
const errorColumnDef = getRowNumberErrorColumnDef(rowErrors, scopeColValue);
Object.assign(cell, computeCellTypeAttributes(rowData, errorColumnDef, types));
} // adding cell indexes to state.indexes
// Keeping the hash for backward compatibility, but we need to have 2 indexes, 1 for columns and one for rows,
// because of memory usage and also at certain point we might have the data but not the columns
state.indexes[row.key][colKeyValue] = [rowIndex, colIndex];
currentRow.push(cell);
return currentRow;
}, row.cells);
prev.push(row);
return prev;
}, []);
}
updateColumns(columns) {
const {
state
} = this;
const hadTreeDataTypePreviously = hasTreeDataType(state); // calculate cell to focus next before indexes are updated
setCellToFocusFromPrev(state);
normalizeColumns(state, columns, this.privateTypes);
setDirtyValues(state, this._draftValues);
updateRowNavigationMode(hadTreeDataTypePreviously, state);
state.headerIndexes = generateHeaderIndexes(getColumns(state));
updateHeaderActions(state);
*this.updateRowsAndCellIndexesNew(state);*
updateSelectionState(state);
adjustRowNumberColumnWidth(this.template, state);
updateColumnWidthsMetadata(state); // set the celltofocus next to null if the column still exists after indexes calculation
updateCellToFocusFromPrev(state);
if (getColumns(state).length !== getColumnsWidths(state).length) {
resetColumnWidths(state);
if (getData(state).length > 0) {
// when there are column changes, update the active cell
syncActiveCell(state);
}
} else if (hasDefinedColumnsWidths(state)) {
// try to adjust column size if previous size in the state and table is visible (not hidden)
if (isTableRenderedVisible(this.template)) {
adjustColumnsSize(this.template, state);
} else {
adjustColumnsSizeFromState(state);
}
}
if (state.keyboardMode === 'NAVIGATION') {
updateTabIndexActiveCell(state);
updateTabIndexActiveRow(state);
}
if (state.cellToFocusNext && state.activeCell) {
setFocusActiveCell(this.template, this.state);
}
}
isDataRowDisabled(state, rowKey) {
if(state) {
let isDisabled = 'isDisabled'; //This is a field in your data from apex (i always wrapper sobjects into a class)
let keyField = state.keyField;
let data = state.data;
let matchedDataRow = data.find( y => y[keyField] === rowKey);
return (matchedDataRow && matchedDataRow[isDisabled]);
}
return false;
}
}