Maximize User Interaction: GraphQL, LWC, and Toasts

This blog proposes a solution for improving user experience in Salesforce development. It addresses the problem of unclear messages when users don't have access to specific records. The solution includes using GraphQL for efficient data fetching, Lightning Web Components for a responsive interface, the onrowaction event in lightning-datatable for smoother interactions, and custom toast messages for clearer communication. These strategies aim to create efficient and user-friendly applications.

maximize-user-interaction-graphql-lwc-toast

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://help.salesforce.com/s/articleView?id=release-notes.rn_lwc_graphql_wire.htm&release=242&type=5

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.

Arun Kumar
Arun Kumar

Arun Kumar is a Salesforce Certified Platform Developer I with over 7+ years of experience working on the Salesforce platform. He specializes in developing custom applications, integrations, and reports to help customers streamline their business processes. Arun is passionate about helping businesses leverage the power of Salesforce to achieve their goals.

Articles: 162

Leave a Reply

Discover more from SFDC Lessons

Subscribe now to keep reading and get access to the full archive.

Continue reading

Discover more from SFDC Lessons

Subscribe now to keep reading and get access to the full archive.

Continue reading