3

I need to create a link to an object within a data-table listing. For example, list of contacts on a data table, when the user clicks the "name", it opens that Contact record view. This seems simple enough, and yet, I'm having trouble finding a solution or example anywhere out on the internet or trailhead. It seems like all the solutions I find are using "tiles" and cascading events. I don't need anything that complicated.

Let's take for example: https://sfdccoder.wordpress.com/2019/02/21/lightning-web-component-load-contacts-list-example/

<template>
<lightning-card title="Contact ListView" icon-name="custom:custom67">

    <template if:true={listView.data}>
        <div class="slds-m-around_medium">
        <template for:each={contacts} for:item="contact">
            <p key={contact.Id}>{contact.fields.Name.value} – {contact.fields.Phone.value} </p>
        </template>
        </div>
    </template>

    <template if:true={listView.error}>
        Error in loading the data ..
    </template>

</lightning-card>

In this example, they are not making the name a link -- I would like to make it a link that would open that contact's record view.

Assuming I were to add the link using something like this:

<p key={contact.Id}>
<lightning-button variant="base" label={contact.fields.Name.value} title="View Contact" onclick={viewContact}></lightning-button> 
– {contact.fields.Phone.value}

What would the "viewContact" js look like?

Something along the lines of:

viewContact() {
    this[NavigationMixin.Navigate]({
        type: 'standard__recordPage',
        attributes: {
            recordId: ?????,
            objectApiName: 'Contact',
            actionName: 'view'
        }
    });
}

I don't understand how to pass the contact.Id value from the HTML to the JS (?????) Or, using:

<lightning-formatted-url value={?????} tooltip="View Contact" label={contact.fields.Name.value} target="_blank"></lightning-formatted-url>

(I realized the button isn't what I wanted)

In this example, in the past I would have just put "/{contact.Id}" -- but that doesn't work with the lightning web component : it gives me the error: Ambiguous attribute value value="/{c.Id}". If you want to make it a string you should escape it value="/{c.Id}" -- and if I escape it using /, then the link it forms is incorrect.

UPDATE: Per answer below, can pass ID using "value" and/or "data-*" My full updated code is below for future reference:

sesResultsContact.js

import { NavigationMixin, CurrentPageReference } from 'lightning/navigation';
import { LightningElement, wire } from 'lwc';

import getContactList from '@salesforce/apex/sesController.getContactList';

export default class SesResultsContact extends NavigationMixin(LightningElement) {
@wire(CurrentPageReference) pageRef;
@wire(getContactList) contacts;

viewContact(event) {
    this[NavigationMixin.Navigate]({
        type: 'standard__recordPage',
        attributes: {
            recordId: event.target.value,
            objectApiName: 'Contact',
            actionName: 'view'
        }
    });
}

viewContact2(event) {
    this[NavigationMixin.Navigate]({
        type: 'standard__recordPage',
        attributes: {
            recordId: event.target.dataset.id,
            objectApiName: 'Contact',
            actionName: 'view'
        }
    });
}

}

sesResultsContact.html

<template>
<lightning-card title="Contact" icon-name="standard:contact">
    <div class="slds-card__body_inner">

        <template if:true={contacts.data}>
            <table class="slds-table slds-table_fixed-layout slds-table_bordered">
                <thead>
                    <tr class="slds-line-height_reset">
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Name">Name</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Email">Email</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Phone">Phone</div>
                        </th>
                        <th class="" scope="col">
                            <div class="slds-truncate" title="Account">Account</div>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <template for:each={contacts.data} for:item="c">
                        <tr class="slds-hint-parent" key={c.Id}>
                            <th data-label="Name" scope="row" class="slds-truncate">
                                <lightning-button-icon icon-name="utility:search" variant="bare"
                                    alternative-text="Open record" onclick={viewContact} value={c.Id}>
                                </lightning-button-icon>
                                <a onclick={viewContact2} title="View Contact 2" data-id={c.Id}
                                    target="_blank">{c.Name}</a>
                            </th>
                            <td data-label="Email">
                                <div class="slds-truncate" title={c.Email}>
                                    <lightning-formatted-email value={c.Email}></lightning-formatted-email>
                                </div>
                            </td>
                            <td data-label="Phone">
                                <div class="slds-truncate" title={c.Phone}>
                                    <lightning-formatted-phone value={c.Phone}></lightning-formatted-phone>
                                </div>
                            </td>
                            <td data-label="Account">
                                <div class="slds-truncate" title={c.Account.Name}>
                                    <lightning-formatted-url value={c.AccountId} label={c.Account.Name}
                                        target="_blank"></lightning-formatted-url>
                                </div>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>

        </template>

        <template if:true={contacts.error}>
            <c-error-panel errors={contacts.error}></c-error-panel>
        </template>

        <div class="slds-align_absolute-center">
            <lightning-button label="Show More" variant="base" disabled> </lightning-button>
        </div>

    </div>
</lightning-card>

sesController.cls

public with sharing class sesController {

@AuraEnabled(cacheable=true)
public static List<Contact> getContactList() {
    return [ SELECT Id, Name, Phone, Email, AccountId, Account.Name 
             FROM Contact 
             WHERE Email != null 
             ORDER BY Name
             LIMIT 1];
}

}

1
  • @glls Thanks... I tried that, and I get an error when clicking on the link: [Cannot read property 'Id' of undefined] m.viewContact()@https://.../lightning/n/modules/c/sesResultsContact.js:4:2887
    – Paul N
    Mar 28, 2019 at 16:17

2 Answers 2

10

Although at first I fought the idea of creating a component "just" to display a link... I came up with this solution which will generate a navigation link using parameters passed to the component. It even creates the HREF URL so the user can right-click and open the link in a new window or copy the link. It's generic so it can be used to generate any navigation link, and seems pretty simple and elegant.

navigationLink.js

// *********************************************************************
// ** NavigationLink
// *********************************************************************
// ** Written by: Paul J. Narsavage <NarsavageP@dnb.com>
// ** Date: Mar 28, 2019
// ** Purpose: Generates a navigation link using parameters passed to the component
// ** Reference: https://developer.salesforce.com/docs/component-library/documentation/lwc/use_navigate
// **********************************************************************
//  1/25/22 PJN - Adjust to use "renderedCallback" instead of "connectedCallback" in order to keep link updated if changed
//  1/26/22 PJN - Prevent issues if no record-id is passed

/* Component Parameters:

    <c-navigation-link
        label="The Label"
        title="The Title"
        type="PageReferenceType"
        record-id="RecordId"
        api-name="ApiName"
        object-api-name="ObjectApiName"
        relationship-api-name="RelationshipApiName"
        page-name="PageName"
        action-name="ActionName"></c-record-link>
*/

import { LightningElement, api, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';

export default class NavigationLink extends NavigationMixin(LightningElement) {
    @api label; // Text to be displayed as the link
    @api title; // Text to be displayed when hovering on the link (optional, will default to label)

    @api type;               // PageReference Type (default of "standard__recordPage" if recordId provided)
    @api recordId;           // Id of the record
    @api pageName;           // The name of the Page
    @api apiName;            // API Name of Page
    @api objectApiName;      // Object type
    @api relationshipApiName // The API Name of Relationship to open
    @api actionName;         // Action to perform when clicked (default of "view" if recordId provided)

    @api replace; // Indicates how the link that is created will affect browser
    //  no [default, normal, open in current window]
    //  yes [replaces current browser history entry]
    //  new [opens in a new window; url only]
    get urlOnly() { return (this.replace === "new"); }

    navigationLinkRef = null;
    @track url;

    renderedCallback() {

        // Set defaults...
        if (!this.title && this.label) this.title = this.label;
        if (this.recordId) {
            if (!this.type) this.type = "standard__recordPage";
            if (!this.actionName) this.actionName = 'view';
        }
        if (!this.replace) this.replace = 'no';

        if (!this.type) return;

        // Generate the page reference for NavigationMixin...
        this.navigationLinkRef = {
            type: this.type,
            attributes: {
                recordId: this.recordId,
                pageName: this.pageName,
                apiName: this.opiName,
                objectApiName: this.objectApiName,
                relationshipApiName: this.relationshipApiName,
                actionName: this.actionName
            }
        };

        // Set the link's HREF value so the user can click "open in new tab" or copy the link...
        if (this.navigationLinkRef) this[NavigationMixin.GenerateUrl](this.navigationLinkRef)
            .then((url) => { this.url = url });

    }//renderedCallback

    handleClick(event) {
        if (!this.navigationLinkRef) return;

        var doReplace = (this.replace === "yes");

        // Stop the event's default behavior (don't follow the HREF link) and prevent click bubbling up in the DOM...
        event.preventDefault();
        event.stopPropagation();

        // Navigate as requested...
        this[NavigationMixin.Navigate](this.navigationLinkRef, doReplace);

    }//handleClick

}

navigationLink.html

<template>
        <a if:true={urlOnly} href={url} target="_blank" title={title}>{label}</a>
        <a if:false={urlOnly} href={url} onclick={handleClick} title={title}>{label}</a>
</template>

Examples for usage:

<c-navigation-link label="Chatter" title="Navigate to a standard page. In this example, Chatter home."                 
    type="standard__namedPage" page-name="chatter"></c-navigation-link>
<c-navigation-link label="Home" title="Navigate to a standard page. In this example, the Home page."                
    type="standard__namedPage" page-name="home"></c-navigation-link>
<c-navigation-link label="Hello" title="Navigate to a custom page. In this example, the Hello tab."                  
    type="standard__navItemPage" api-name="Hello"></c-navigation-link>
<c-navigation-link label="Files Home" title="Navigate to an object home page. In this example, the Files home."           
    type="standard__objectPage" action-name="home" object-api-name="ContentDocument"></c-navigation-link>
<c-navigation-link label="List View" title="Navigate to a list view. In this example, Contacts list."                    
    type="standard__objectPage" object-api-name="Contact" action-name="list"></c-navigation-link>
<c-navigation-link label="New Contact" title="Navigate to a new record page. In this example, a new contact page."         
    type="standard__objectPage" object-api-name="Contact" action-name="new"></c-navigation-link>
<c-navigation-link label="View Contact" title="Navigate to a record page. In this example, a contact."                      
    type="standard__recordPage" record-id="003a000002UkPDtAAN"></c-navigation-link>
<c-navigation-link label="Edit Contact" title="Navigate to a edit record page. In this example, a contact."                 
    type="standard__recordPage" record-id="003a000002UkPDtAAN" action-name="edit"></c-navigation-link>
<c-navigation-link label="Account Contacts" title="Navigate to a related list. In this example, an Account's Related Contacts." 
    type="standard__recordRelationshipPage" record-id="001a000001s8zS1AAI" object-api-name="Account" relationship-api-name="Contacts" action-name="view"></c-navigation-link>
10
  • This is great, thanks for sharing.
    – H Alves
    Aug 15, 2019 at 14:31
  • 1
    @compski That is the recommended naming convention: developer.salesforce.com/docs/component-library/documentation/…
    – Paul N
    Oct 17, 2019 at 12:52
  • 1
    @PaulN I solved the issue with the related lists the other day (I misdirected my issue a little bit), the examples for usage just have a typo with how to call them. On your last example for Account Contacts the type should be set to "standard__recordRelationshipPage" instead of "standard__recordPage". The navigation mixin requires that type to push to a related list page. Otherwise fantastic component I've been extending it recently and it has been great!
    – tokyo0709
    Feb 5, 2020 at 20:50
  • 1
    @tokyo0709, thanks, I fixed the example. Glad it has been helpful.
    – Paul N
    Feb 6, 2020 at 16:45
  • 1
    @NishantSinghPanwar
    – Paul N
    Jul 3, 2020 at 19:37
7

you can only get the event.target.value of an element apparently, thus your button would look somewhat similar to :

<template  for:each={contacts.data} for:item="contact">
    <lightning-button key={contact.Id} variant="base" label={contact.Name} title="View Cotnact" onclick={handleContactView} value={contact.Id}></lightning-button>
</template>

and your controller method:

handleContactView(event) {
    // Navigate to contact record page
    this[NavigationMixin.Navigate]({
        type: 'standard__recordPage',
        attributes: {
            recordId: event.target.value,
            objectApiName: 'Contact',
            actionName: 'view',
        },
    });
}

you could as well use a different approach, breakdown your components into smaller modules, use events to communicate (and you can pass around the contact object) then you can reference it as such - event.target.contact.Id.

I found this project on Trailhead Build a Bear-Tracking App with Lightning Web Components to be very helpful for understanding the basics (alongside the other LWC modules)

7
  • Thanks! This is very helpful, and I'm getting closer... In debug, I can see that I'm able to pass the Id using "value" or "data-*"... But now I'm stumped with the error [this[i.NavigationMixin.Navigate] is not a function] I did the Build-a-Bear training, and saw how to do using components and passed events - but I really didn't feel that it was appropriate to create a child component and pass events for a single link (for just a listing of names). The child component would just be a link... that's a whole lot of overhead and extra components to maintain just for a link?
    – Paul N
    Mar 28, 2019 at 16:49
  • did you import NavigationMixin from lightinng/navigation ?
    – glls
    Mar 28, 2019 at 16:51
  • Yes, I did. I can post full code in reply below...
    – Paul N
    Mar 28, 2019 at 16:52
  • no - please =P instead, update your original post
    – glls
    Mar 28, 2019 at 16:52
  • fyi: the error might be due to you not extnding NavigationMixin, you need to extend it and pass LightingElement: extends NavigationMixin(LightningElement)
    – glls
    Mar 28, 2019 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.