Lightning Web Components (LWC) Lifecycle Hooks and Using third-party JavaScript libraries

Lightning Web Components (LWC) Lifecycle Hooks and Using third-party JavaScript libraries

Reference:
https://developer.salesforce.com/docs/component-library/documentation/en/lwc/create_lifecycle_hooks

Sometimes we get uses cases where we need to use third-party JavaScript libraries such as C3 (https://c3js.org/), I will go through the steps needed to configure this below using a use case:

  1. Download latest C3 libraries that include .js and .css files
  2. Download latest D3 libraries that include .js files.
  3. Create static resources for each of these in Salesforce.
  4. Create LWC component that uses these to render graphs from C3.

LWC provides following lifecycle Hooks

constructor() :
This method fires when a component instance is created.
The constructor flows from parent to child.
For more information on this method https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_created

connectedCallback()
This method fires when a component is inserted into the DOM.
This method flow from parent to child. You can’t access child elements from the callbacks because they don’t exist yet
For more information on this method https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_dom

renderedCallback()
Use it to perform logic after a component has finished the rendering phase.
This method flows from child to parent.
A component is usually rendered many times during the lifespan of an application.
Use Boolean field to track whether renderedCallback() has been executed and to only perform the operation if it has not been rendered.
For more information on this method https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_rendered
For this blog post I will focus on this method and how to use third-party library

disconnectedCallback()
This method fires when a component is removed from the DOM.
For more information on this method https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_dom

errorCallback()
This method captures errors in all the descendant components.
For more information on this method https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_lifecycle_hooks_error

Use Case

I used a use case for COVID vaccination site, where people might signup for vaccination. In our use case vaccination site remains open for certain period of times
like Campaign in Salesforce, for this reason I used Campaign object. To track daily vaccination and signups I used custom object and called it Shift
So for each day there is a corresponding shift record for the time period Campaign might be open. Each shift has start and end times and number of people signed up to get vaccinated per day. Shifts have following statuses
Open – When shift is open for signup. but not actually started administering vaccination
In Progress – Closed for signup and administration of vaccination started.
Closed – End of day when shift is closed
Suspended – If shift needs to be suspended for some reason.
Cancelled – If Shift needs to be cancelled.

For above use case I developed LWC and used carts from C3 library to display visually progress of vaccination campaign for a particular location.

Reference Code

@AuraEnabled(cacheable=true)
public static  Double getPercentVacinated(String recordId) {

List<AggregateResult> aggregateResults = [SELECT SUM(Actual_Vaccinated__c)ActualVaccinated, SUM(Number_Signed_Up__c)SignedUp  FROM Shift__c WHERE Campaign__c = :recordId AND Status__c IN ('Closed','In Progress') WITH SECURITY_ENFORCED];
        
Integer t = ((Decimal)aggregateResults[0].get('SignedUp')).intValue();
Integer a = ((Decimal)aggregateResults[0].get('ActualVaccinated')).intValue();
Double  p = (a*100)/t;

return p;
}
<template>
    <lightning-card title="Actual Response (%)">
        <!-- When using these external libraries thayt manuplate DOM in a Lightning web component, use lwc:dom="manual" -->
        <div lwc:dom="manual" class="c3"></div>
        
        <div slot="footer">
            <lightning-badge label="In Progress"></lightning-badge>
            <lightning-badge label="Closed"></lightning-badge>
        </div>
        <div class="slds-align_absolute-center" style="height:5rem">
            {dateTimeNow}
        </div>
    </lightning-card>
</template>
import { LightningElement, api,wire, track, } from 'lwc';

// Import Toast
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

// To import a third-party JavaScript or CSS library, use the platformResourceLoader module.
import { loadStyle, loadScript } from 'lightning/platformResourceLoader';

// Import Static Resources
import D3    from '@salesforce/resourceUrl/D3';
import C3    from '@salesforce/resourceUrl/C3';
import C3CSS from '@salesforce/resourceUrl/C3CSS';

// Import Custom Apex
import PercentVacinated from '@salesforce/apex/VaccinationController.getPercentVacinated';

renderedCallback() : Below is code I used to render graphs in side renderedCallback method

   /**
    * renderedCallback()
    * Perform logic after a component has finished the rendering phase
    */
    renderedCallback() {
        if(this.librariesLoaded){
            this.renderGraph();
        }
        if(!this.loadingInitialized) {
            this.loadingInitialized = true;
            Promise.all([
                loadScript(this, D3 ),
                loadScript(this, C3 ),
                loadStyle(this, C3CSS )           
            ]).then(() => {
                this.librariesLoaded = true;
                this.renderGraph();
            }).catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error loading C3',
                        message: error.message,
                        variant: 'error',
                    }),
                );
            });
        }
    }

renderGraph() : In this method use C3 library to display graph, this method is called from renderedCallback()

/**
    * renderGraph()
    * Renders guage graph
    */
    renderGraph() {
        this.chart = c3.generate({
            bindto: this.template.querySelector('div.c3'),
            data: {
                columns: [
                    ['Vacinated', this.percentVacinated]
                ],
                type: 'gauge',
            },
            gauge: {
                label: {
                    show: true,
                    format: function (value, ratio) {
                        return value;
                    }
                  },
                  min: 0,
                  max: 100,
                  units: '%',
                  width: 25

            },

            interaction: {
                enabled: false
            },
            size: {
                height: 180
            }
        });
    }

Third party libraries used

https://d3js.org/

https://c3js.org/