
Introduction
In Salesforce development, ensuring a great user experience is vital for the success of an application. Developers often encounter a challenge: unclear messages when users can’t access certain records. This blog discusses a solution that uses GraphQL to fetch records efficiently, makes use of the onrowaction event in lightning-datatable for smooth user interactions, and improves the overall user experience with custom toast messages.
Problem Statement
When a user tries to access a record without the necessary permissions, the platform displays a generic message that might not be user-friendly. To address this, we’ll delve into a Lightning Web Component (LWC) solution that not only fetches records efficiently using GraphQL but also provides a more user-friendly message when access is denied.
The HMTL template
<template>
<lightning-card label="Data Table" icon-name="standard:account">
<lightning-datatable key-field="id"
data={accounts}
columns={columns}
onrowaction={handleRowAction}
hide-checkbox-column="true">
</lightning-datatable>
</lightning-card>
</template>
The JS Controller :
Fetching Records with GraphQL
The Lightning Web Component GraphqlExample uses GraphQL to fetch a list of accounts. The @wire decorator is employed to make a GraphQL query, and the graphqlQueryResult method processes the returned data. The fetched records are then transformed into a format suitable for display in a Lightning DataTable. Learn more here about graphQL wire adapter.
import { LightningElement, wire } from 'lwc';
import { gql, graphql } from "lightning/uiGraphQLApi";
import hasRecordAccess from '@salesforce/apex/GraphqlExampleController.doesUserHaveRecordAccess';
import loggedInUserID from '@salesforce/user/Id';
import { NavigationMixin } from 'lightning/navigation';
import Utility from './utility';
let utility;
const columns = [
{
label: 'Name',
fieldName: 'recordLink',
type: 'button',
sorttable: true,
typeAttributes: { label: {fieldName: 'Name'}, tooltip: 'Name', name:'view_account', variant:'base'}
},
{
label: 'Website',
fieldName: 'Website',
type: 'text',
sorttable: true
},
{
label: 'Phone',
fieldName: 'Phone',
type: 'text',
sorttable: true
}
];
export default class GraphqlExample extends NavigationMixin(LightningElement) {
myRecId = '';
accounts = [];
columns;
hasRecordAccess = false;
connectedCallback(){
utility = new Utility();
}
@wire(graphql, {
query: gql`
query getAccounts {
uiapi {
query {
Account(
first: 10
) {
edges {
node {
Id
Name { value }
Website { value }
Phone { value }
}
}
}
}
}
}
`,
operationName: 'getAccounts',
})
graphqlQueryResult({ data, errors }) {
if (data) {
console.log('Data result: ' + JSON.stringify(data));
let result = data.uiapi.query.Account.edges.map((edge) => ({
Id: edge.node.Id,
Name: edge.node.Name.value,
Website: edge.node.Website.value,
Phone: edge.node.Phone.value
}));
console.log('Result: '+ JSON.stringify(result));
for(let i=0; i < result.length; i++){
let tempRecord = Object.assign({}, result[i]);
tempRecord.recordLink = "/" + tempRecord.Id;
this.accounts.push(tempRecord);
}
this.columns = columns;
console.log('Final list : '+ JSON.stringify(this.accounts));
}else{
console.log(errors);
}
}
handleRowAction(event){
const actionName = event.detail.action.name;
const row = event.detail.row;
if(actionName === 'view_account'){
console.log('Record ID : '+ row.Id);
hasRecordAccess({accountId: row.Id, userId: loggedInUserID})
.then(result => {
console.log('Has record access: ' + result);
if(result){
//if user has record access
this[NavigationMixin.Navigate]({
type: 'standard_recordPage',
attributes: {
recordId: row.Id.slice(0,-3),
actionName: 'view'
}
});
}else{
//show toast message that you don't have access
utility.displayToast('Warning', 'You do not have access to see record', 'warning');
}
})
}
}
}
User-Friendly Record Access Check
When a user clicks on a record in the Lightning DataTable, the handleRowAction method is triggered. It checks if the user has access to the selected record using an Apex method (doesUserHaveRecordAccess from GraphqlExampleController). If access is granted, the user is navigated to the record page; otherwise, a custom toast message is displayed.
handleRowAction(event) {
const actionName = event.detail.action.name;
const row = event.detail.row;
if (actionName === 'view_account') {
// Check if the user has access to the record
hasRecordAccess({ accountId: row.Id, userId: loggedInUserID })
.then(result => {
if (result) {
// Navigate to the record page if user has access
this[NavigationMixin.Navigate]({
type: 'standard_recordPage',
attributes: {
recordId: row.Id,
actionName: 'view'
}
});
} else {
// Display a user-friendly toast message if access is denied
utility.displayToast('Warning', 'You do not have access to see the record', 'warning');
}
});
}
}
The Apex Contoller
Efficient Record Access Check
To efficiently check whether the user has access to a record, the Apex class GraphqlExampleController provides the doesUserHaveRecordAccess method. This method utilizes the UserRecordAccess object to determine if the user has read access to the specified record.
public with sharing class GraphqlExampleController {
@AuraEnabled(cacheable=true)
public static Boolean doesUserHaveRecordAccess(String accountId, String userId){
try {
List<UserRecordAccess> userRecAccessList = [SELECT RecordId, HasReadAccess from UserRecordAccess WHERE UserId= : userId AND RecordId=: accountId];
return !userRecAccessList.isEmpty() ? userRecAccessList[0].HasReadAccess : false;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
The Utility JS file
Enhancing User Experience with Toast Messages
To enhance the user experience, a utility class (Utility) is used to display toast messages. The displayToast method creates a ShowToastEvent and dispatches it, providing a consistent and user-friendly way to communicate important information.
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
let mainObj;
export default class Utility{
constructor(sMain){
mainObj = sMain;
}
//show toast message
displayToast(title, message, variant){
const toastEvent = new ShowToastEvent({
title,
message,
variant
});
mainObj.dispatchEvent(toastEvent);
}
}
Conclusion
By combining the power of GraphQL for efficient data fetching and Lightning Web Components for a responsive and interactive user interface, we’ve addressed the issue of displaying user-friendly messages when access to records is denied. The onrowaction event in lightning-datatable ensures seamless user interactions, and custom toast messages enhance the overall user experience. This solution not only solves a specific problem but also showcases best practices in Salesforce development for creating efficient and user-friendly applications.
References:
https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/example
About the blog
SFDCLessons is a blog where you can find various Salesforce tutorials and tips that we have written to help beginners and experienced developers alike. we also share my experience and knowledge on Salesforce best practices, troubleshooting, and optimization. Don’t forget to follow us on:
Newsletter
Subscribe to our email newsletter to be notified when a new post is published.
