How to Add Hyperlink and icons in Lightning:datatable

Lightning, Lightning Component, Lightning Tutorials, Lightning Web Components

Hey Everyone,

Welcome again. In this blog post, we are going to learn how we can add hyperlink in datatable and how to add custom icons based on some conditions in lightning datatable

Use Case: – Business wants to display the list of top 10 cases in a datatable. There are few catches here that are given below

  1. CaseNumber & Subject Field must be hyperlink and when click on those it should redirect to case detail page.
  2. Contact Name & Account Name field also should be hyperlink field and clicking on it should redirect to contact or account record.
  3. An icon should be displayed left to priority field and icon should be changed based on priority.

Now, as we have got the use case. Let’s see what all it will take to develop.

  1. An Apex class
  2. Lightning Web Component

Before we directly jump into the final outcome, we will break the requirement into multiple steps. So, let’s create the apex class first.

Use below code and Name it as CaseLWCService

public with sharing class CaseLWCService {
    @AuraEnabled
    public static List<Case> fetchCases(){
        return [SELECT Id, CaseNumber, Subject, Description, AccountId, Account.Name, ContactId,
        Contact.Name, Status, Priority 
        FROM CASE
        LIMIT 10];
    }
}

We have got our apex class which will return the Top10 Case records.

Now, let’s develop the component which will be using Lightning:datatable for displaying the records. Use below code for .html file

<template>
    <lightning-card  variant="Narrow"  title="Case List" icon-name="standard:case">
        <div class="slds-m-around_small">
            <lightning-datatable
                key-field="id"
                data={result}
                show-row-number-column
                hide-checkbox-column
                columns={columnsList}
                onrowaction={handleRowAction}>
            </lightning-datatable>
        </div>
    </lightning-card>
</template>

We have got the UI part, however we need to have the associated JavaScript class so that we can pull the data from Apex Class and that data can be displayed into component. Below is the code for the JS Class.

import { LightningElement, wire, api, track } from 'lwc';
import fetchCases from '@salesforce/apex/CaseLWCService.fetchCases';

const columns = [
    { label: 'CaseNumber', fieldName: 'CaseNumber' },
    { label: 'Subject', fieldName: 'Subject', wrapText: true},
    { label: 'Status', fieldName: 'Status' },
    { label: 'Priority', fieldName: 'Priority' },
    { label: 'Contact', fieldName: 'ContactName', wrapText: true },
    { label: 'Account', fieldName: 'AccountName', wrapText: true }
];
export default class CaseDatatable extends LightningElement {
    
    @api result;
    @track error;

    columnsList = columns;
    
    connectedCallback(){
        this.getAllCaseDetails();
    }

    getAllCaseDetails(){
        fetchCases()
            .then(data => {
                /* Iterate with Each record and check if the Case is Associated with Account or Contact
                    then get the Name and display into datatable
                */
                data.forEach(caseRec => {
                    if(caseRec.ContactId){
                        caseRec.ContactName = caseRec.Contact.Name;
                    }
                    if(caseRec.AccountId){
                        caseRec.AccountName = caseRec.Account.Name;
                    }
                });
                this.result = data;
                window.console.log(' data ', data);
                this.error = undefined;
            })
            .catch(error => {
                this.error = error;
                window.console.log(' error ', error);
                this.result = undefined;
            });
    }
    
    handleRowAction(){
        
    }
}

and below is the code for meta file

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Case Details</masterLabel>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
</LightningComponentBundle>

We have developed the component and displayed the case list into datatable. However, we are still remaining with few point. If you will see the output of the above component will look like below

If you notice the above screenshot, there is no hyperlink shown for any of the fields which were mentioned in the use case.

Let’s modify the columns of datatable to display the link.

We are going to use same column which is CaseNumber and Subject. To display the url what we need to do is specify the type for the column like below

{ label: 'CaseNumber', fieldName: 'CaseNumber', type:'url' },

However, doing this will not work. What we need to do is to add some attributes in the same column under typeAttributes so that we can specify the text which we wanted to display. See code below, inside typeAttributes we can specify the target as well and many more. Learn Here

typeAttributes: {
           label: { fieldName: 'CaseNumber' },
           target : '_blank'
       }

Now, if we merge the code it will look like below

{ 
        label: 'CaseNumber', fieldName: 'CaseNumber', type:'url',
        typeAttributes: {
            label: { 
                fieldName: 'CaseNumber' 
            },
            target : '_blank'
        }
    },

We have prepared the column and there is one more thing remaining which is the actual URL. As if you noticed on the above code we are using CaseNumber in both places and the record details url look like below

https://+host+/+recordId

So, we have to prepare the URL and a new field so that we can use that new field in place of CaseNumber in the first place of fieldName: ‘CaseNumber’, type:’url’,

We will follow the same approach for the rest 3 fields ( Subject, Contact Name & Account Name). That means we will be needing 3 New fields which will contain the record detail page Link.

Now, here is the update code for JavaScript Class with URL/HyperLink in Datatable

import { LightningElement, wire, api, track } from 'lwc';
import fetchCases from '@salesforce/apex/CaseLWCService.fetchCases';

const columns = [
    { 
        label: 'CaseNumber', fieldName: 'caseUrl', type:'url',
        typeAttributes: {
            label: { 
                fieldName: 'CaseNumber' 
            },
            target : '_blank'
        }
    },
    { 
        label: 'Subject', fieldName: 'caseUrl', wrapText: true,
        type: 'url',
        typeAttributes: {
            label: { 
                fieldName: 'Subject' 
            },
            target : '_blank'
        }
    },
    { label: 'Status', fieldName: 'Status' },
    { label: 'Priority', fieldName: 'Priority' },
    { 
        label: 'Contact', fieldName: 'ContactUrl', wrapText: true,
        type: 'url',
        typeAttributes: {
            label: { 
                fieldName: 'ContactName' 
            },
            target : '_blank'
        } 
    },
    { 
        label: 'Account', fieldName: 'AccountUrl', wrapText: true,
        type: 'url',
        typeAttributes: {
            label: { 
                fieldName: 'AccountName' 
            },
            target : '_blank'
        } 
    }
];
export default class CaseDatatable extends LightningElement {
    
    @api result;
    @track error;

    columnsList = columns;
    
    connectedCallback(){
        this.getAllCaseDetails();
    }

    getAllCaseDetails(){
        fetchCases()
            .then(data => {
                /* Iterate with Each record and check if the Case is Associated with Account or Contact
                    then get the Name and display into datatable
                */
                /* Prepare the Org Host */
                let baseUrl = 'https://'+location.host+'/';
                data.forEach(caseRec => {
                    caseRec.caseUrl = baseUrl+caseRec.Id;
                    if(caseRec.ContactId){
                        caseRec.ContactName = caseRec.Contact.Name;
                        /* Prepare Contact Detail Page Url */
                        caseRec.ContactUrl = baseUrl+caseRec.ContactId;
                    }
                    if(caseRec.AccountId){
                        caseRec.AccountName = caseRec.Account.Name;
                        /* Prepare Account Detail Page Url */
                        caseRec.AccountUrl = baseUrl+caseRec.AccountId;
                    }
                });
                this.result = data;
                window.console.log(' data ', data);
                this.error = undefined;
            })
            .catch(error => {
                this.error = error;
                window.console.log(' error ', error);
                this.result = undefined;
            });
    }
    
    handleRowAction(){
        
    }
}

If you will preview the code, you will see the screen like below

We are done with 75% of the requirement however still we need to display the icons next to Priority based on priority. For this, we need to use cellAttributes property of the datatable column instead of typeattributes.

Let;s see how to display the icon and we can get the icons from here

{
        label: 'Priority', fieldName: 'Priority',
        cellAttributes: { 
            iconName: 'utility:sentiment_negative', 
            iconAlternativeText: 'Priority' 
        }
    },

If you have noticed, we are using icon name which is static and will display the same icon. So to display the icon dynamically we need to generate the icon in JS and use like below

{
        label: 'Priority', fieldName: 'Priority',
        cellAttributes:{ 
            iconName: { 
                fieldName: 'priorityIcon' 
            },
            iconPosition: 'left', 
            iconAlternativeText: 'Priority Icon' 
        }
    },

Below is the complete code for the blog

If you will preview the code you will see the output like below

Thanks for Reading. Sharing is Caring 🙂

If you have any questions or suggestions, please feel free to reach out to me.

3 comments

  • Hello Team,

    Thank you for nice blog. I learnt something out of it. I tried to modify for Account sObject but once i add to Builder I am not able to see records, can you please where I am missing:

    Class Logic:

    @AuraEnabled(cacheable=true)
    public static List getAccountList(){
    return [select id, Name, AccountNumber, Phone, Rating from Account
    LIMIT 5];
    }

    JS file:

    import { LightningElement, api, track } from ‘lwc’;
    import getAccountList from ‘@salesforce/apex/AccountController.getAccountList’;
    import ACCOUNTNUMBER_FIELD from ‘@salesforce/schema/Account.AccountNumber’;
    import PHONE_FIELD from ‘@salesforce/schema/Account.Phone’;
    import RATING_FIELD from ‘@salesforce/schema/Account.Rating’;
    const COLUMNS = [

    { label: 'Account', fieldName: 'AccountUrl', wrapText: true,
    type: 'url',
    typeAttributes: {
    label: {
    fieldName: 'AccountName'
    },
    target : '_blank'
    }
    },
    { label: 'Account Number', fieldName: ACCOUNTNUMBER_FIELD.fieldApiName, type: 'text' },
    { label: 'Phone', fieldName: PHONE_FIELD.fieldApiName, type: 'Phone' },
    { label: 'Rating', fieldName: RATING_FIELD.fieldApiName, type: 'Picklist' }

    ];

    export default class retrieveRecord extends LightningElement() {
    @api result;
    @track error;

    columnsList = COLUMNS;

    connectedCallback(){
    this.getAllCaseDetails();
    }

    getAllCaseDetails(){
    getAccountList()
    .then(data => {
    /* Iterate with Each record and check if the Case is Associated with Account or Contact
    then get the Name and display into datatable
    */
    /* Prepare the Org Host */
    let baseUrl = 'https://'+location.host+'/';

    data.forEach(accRec => {
    accRec.AccountUrl = baseUrl+accRec.Id;

    if(accRec.Id){
    accRec.AccountName = accRec.AccountName;
    /* Prepare Account Detail Page Url */
    accRec.AccountUrl = baseUrl+accRec.Id;
    }

    });
    this.result = data;
    window.console.log(' data ', data);
    this.error = undefined;
    })
    .catch(error => {
    this.error = error;
    window.console.log(' error ', error);
    this.result = undefined;
    });
    }

    handleRowAction(){

    }

    }

    HTML:

    meta.xml:

    49.0
    true

    lightning__AppPage
    lightning__RecordPage
    lightning__HomePage

    Please advise what went wrong and how to correct at the earliest.

    Regards,
    HS

  • Hello there,

    in conjunction to my initial post, I have printed the error log in console and got below stacktrace:

    error TypeError: Cannot add property AccountUrl, object is not extensible
    at eval (retrieveRecord.js:4)
    at Array.forEach ()
    at eval (retrieveRecord.js:4)

    Please advise how to correct.

    Thanks.
    HS

    • Hi,

      You are getting this error because @wire method is readonly and you are trying to add property on the result that’s why you are getting the error. Try to call your method from the connectedCallback and the doo the same.

Leave a Reply