People working on laptops

How to extend standard Salesforce Lightning Web Components

by Nikita Verkhoshintcev

Nowadays, there are relatively many Lightning Web Components available that already cover a considerable amount of use cases.

Nevertheless, developers still need to extend the functionality in some spots.

For instance, consider adding custom field types to the data table or dynamically updating its elements' styles.

Unfortunately, we cannot manipulate the DOM of the standard LWC outside of it.

But we can create a new LWC and extend the standard class.

In this post, I will explain how to do just that.

Extend Properties

Consider the following case.

I want to add a new custom type to the data table to render a button with the dynamic label. Pressing that button should emit a custom data table event.

First of all, we need to create an LWC for the button. It accepts the data from the table via the attributes and emits a custom event on click to expose it to the parent components.

// inlineButton.js
import { LightningElement, api } from "lwc";

export default class DatatableInlineButton extends LightningElement {
  @api label;
  @api value;
  @api field;
  @api context;

  handleClick(event) {
    // fire event to send context and selected value to the data table
    this.dispatchEvent(
      new CustomEvent("inlineclick", {
        composed: true,
        bubbles: true,
        cancelable: true,
        detail: {
          data: { context: this.context, value: this.value, field: this.field },
        },
      }),
    );
  }
}
// inlineButton.html
<template>
  <lightning-button
    variant="base"
    label="{value}"
    title="{value}"
    onclick="{handleClick}"
  ></lightning-button>
</template>

Next, we need to create an LWC to extend the standard lightning-datatable component.

Note: Remove the HTML file as you don't need it.

// customDataTable.js
import LightningDatatable from "lightning/datatable";

export default class CustomDataTable extends LightningDatatable {
  constructor() {
    super();
  }
}

We can import standard components from the lightning module and extend its class.

Next, we need to define the custom column type and specify its cell template.

Create the following template within the LWC directory.

// inline-button-template.html
<template>
  <c-inline-button
    label="{typeAttributes.label}"
    value="{typeAttributes.value}"
    field="{typeAttributes.field}"
    context="{typeAttributes.context}"
  ></c-inline-button>
</template>

Once we have the template, we can reference it in the custom types configuration. Here is how the custom table should look now.

// customDataTable.js
import LightningDatatable from "lightning/datatable";
import DatatableInlineButtonTemplate from "./inline-button-template.html";

export default class CustomDataTable extends LightningDatatable {
  static customTypes = {
    inlinebutton: {
      template: DatatableInlineButtonTemplate,
      typeAttributes: ["label", "value", "context", "field"],
    },
  };

  constructor() {
    super();
  }
}

Great, now we can start using a new custom data type.

Given the list of tasks to render in the data table

data = [
  {
    Id: "1",
    OwnerName: "John Appleseed",
  },
];

We can specify the inline button custom type to render it inside the table cell.

columns = [{
	{
		label: 'Assigned to',
		fieldName: 'OwnerName',
		type: 'inlinebutton', // custom type key
		typeAttributes: {
                	field: 'OwnerName' ,
			value: { fieldName: 'OwnerName' },
			context: { fieldName: 'Id' }, // bind data key to retrieve the value
            	},
	},
}];

And you can add a handler to a new custom event that we created earlier.

<c-custom-data-table
  data="{data}"
  columns="{columns}"
  oninlineclick="{handleInlineClick}"
></c-custom-data-table>

Extend Methods

Likewise the properties, you can also extend the methods of the parent class.

For instance, I want to add a title attribute to each row in the table to display the assignee name on the mouse over.

We can manipulate the DOM once it is available after the render.

By default, lightning web components have the "renderedCallback" method for that purpose.

The "lightning-datatable" component already uses that callback to render the template correctly, so we need to extend its functionality to add our logic there.

renderedCallback() {
        super.renderedCallback(); // call parent callback
        const rows = Array.from(this.template.querySelectorAll('tr[data-row-key-value]'));
        rows.map(row => {
            const id = row.dataset.rowKeyValue;
            const dataRow = this.data.find(item => item.Id === id);
            row.setAttribute('title', dataRow.OwnerName) // set asignee name as the row title
        })
}

Conclusion

Like mentioned at the beginning, Salesforce already provides many components out of the box. But occasionally, there is a need to change something or enhance the standard components. That is where extending a component functionality becomes handy.

I hope that you find this post helpful. Happy coding!

Nikita Verkhoshintcev photo

Nikita Verkhoshintcev

Salesforce Consultant

Senior Salesforce and full-stack web developer. I design and build modern Salesforce, React, and Angular applications for enterprises. Usually, companies hire me when complex implementation, custom development and UI is required.

Let's work together!

Contact us today and take your digital end-user experience to the next level.

Contact us