
Hello, and welcome to Another Salesforce Blog! Here I will be posting solutions to problems that I couldn’t find an answer to in hopes of helping those who find themselves stuck when using the Salesforce platform.
User Story
This is a pretty basic Intro to JavaScript problem (and common interview and homework problem!) that we will be solving via Salesforce. We want to input a sentence or phrase and determine whether or not what we have entered is a palindrome. For kicks and giggles, we also want to count the number of each character present in our input.
Background
We will be building a Lightning Web Component to solve this problem. Although this is a simple example, this is an advanced topic, and will require some advanced setup and knowledge on the part of the user.
Solution
Step One – Create a Sentence Object.
First, we need to create a Sentence object. We want to store the sentence as a text field, a Boolean that shows whether or not the sentence is a palindrome, and the characters that will be used in our sentence. Let’s also add fields for the numerical characters 0-9. Each character will be a number field with no trailing zeroes. The fastest way to do this will be through a spreadsheet.

Step Two – Create a Lightning Web Component.
Next, we need to create a Lightning Web Component. This will be our parent component, which will host our dynamic charts, which we will create in part two. We’ll call it palindromeChecker
.
If you haven’t ever set up Visual Studio Code for Salesforce development, check out this Trailhead for more information.
Once our LWC is set up, let’s also create a Lightning App using the Lightning App Builder. First, we’ll need to modify our palindromeChecker.js-meta.xml
file to expose our LWC to the App Builder. Replace the code in your .js-meta.xml
file with the following:
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="palindromicSentences"> <apiVersion>50.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__AppPage</target> <target>lightning__RecordPage</target> <target>lightning__HomePage</target> </targets> </LightningComponentBundle>
Next, go to your Lightning App Builder and create a new app. We’ll call this Palindrome_Checker
, and select a single region. Drag your palindromeChecker
component into the region, save, and activate. We want to do this so we can check our progress as we go.
Step Three – Build HTML File.
Let’s get started on our HTML file for our parent component, palindromeChecker.html
.
For now, we’re going to create an input field and button that will take up the first third of our screen.
<template> <lightning-card title="Palindrome Checker"> <div class="slds-m-left_medium"> <div class="slds-grid slds-gutters"> <div class="slds-col slds-size_1-of-3"> <lightning-input type="text" label="Enter a sentence to check:" placeholder="type here..." required></lightning-input> <lightning-button variant="brand" label="Check" title="Check Sentence" onclick={updateSentence} class="slds-m-left_x-small"></lightning-button> <template if:true={sentenceLoaded}> <div class="slds-col"> <lightning-card title="Is my sentence a palindrome?"> <div class="slds-m-left_medium"> {isPalindrome} </div> </lightning-card> </div> </template> </div> </div> </div> </lightning-card> </template>
We’ll write the updateSentence
function shortly, and we will fill out the rest of our screen in the next tutorial.
Right now, our Lightning App should look something like this:

References:
Lightning-Input
Lightning-Button
Grids in LWC
Margins in LWC
Step Four – Build JavaScript File.
Next, let’s start on our JavaScript file.
First, we’re going to want to import the track
and api
decorators into our file and define a variable for our text input, sentenceText
. We will also define variables for sentence
and error
, which we will use later on. We then want to write our updateSentence
function so that it pulls the value from the lightning-input
block and sends it into our palindromeChecker
.
import { LightningElement, track, api } from 'lwc'; export default class PalindromeChecker extends LightningElement { @track sentenceId @track sentenceText @track sentence @track sentenceLoaded = false; @track isPalindrome; @track error updateSentence(event) { let sent = this.template.querySelector('lightning-input').value; this.sentenceText = sent; this.palindromeChecker(sent); } //code continuing below... }
Next, we’re going to write the function that will check to see if the sentence entered is a palindrome or not. This can be done using some basic array
functionality in JavaScript.
palindromeChecker(sent) { //create map to hold sentence + boolean value let sentenceMap = {}; //create map to hold sentence + map of characters let characterMap = {}; //verify that text input is not null if(sent.length > 0) { //create character map to be used in characterCounter var charMap = {}; //regular expression to remove spaces and punctuation var rgx = /[\W_]/g; //simplify string by making lowercase and replacing all spaces and punctuation with '' var simpleString = sent.toLowerCase().replace(rgx, ''); //send simpleString to characterCounter charMap = this.characterCounter(simpleString); //use built in javacript functions to split the string into an array character by character, //reverse the order of the array, and join the array back into a string var reverseString = simpleString.split('').reverse().join(''); //check to see if the simple string and the reverse string are exactly equal var bool = (reverseString === simpleString); //set variable tracking whether or not sentence is a palindrome this.isPalindrome = bool ? 'Yes! :)' : 'No. :('; //escape single quotes var escapeQuotes = sent.replace(/'/g,'\''); //remove any extra return characters var finalStr = escapeQuotes.replace(/^[\r\n]+|\.|[\r\n]+$/g, ''); //add new key value pairs to map sentenceMap[finalStr] = bool; characterMap[finalStr] = charMap; } else { alert('Please enter a sentence or phrase!'); } //JSON.stringify results for processing in apex... //imperative apex call... }
We will write our Apex class momentarily, but let’s stick with Javascript for now and add a characterCounter
function. This particular function will use some map magic to get it done.
characterCounter(str) { //create a map to hold character and count var counts ={} //create counting variables var char, index, len, count; //loop through string for(index = 0, len = str.length; index<len; ++index) { char = str.charAt(index); //get count for current character //will return undefined if character is not yet known count = counts[char]; //if we have seen char, store that count plus one //if we have not seen char, store one counts[char] = count ? count + 1 : 1; } return counts; }
That’s it! That’s all we can do in our JavaScript file until we build our Apex class. We can test our functionality with some alert()
methods in the meantime.
Step Four – Build Apex Class.
Now, let’s save our sentence to our Sentence__c
custom object so we can keep track of it. We are going to use the JSON.stringify
utility in Javascript to send our information into our Apex class from our JavaScript, so we will add the following lines to our JavaScript file at the end of the palindromeChecker
function:
//set jsonpalindrome variable to the stringified sentenceMap let jsonpalindrome = JSON.stringify(sentenceMap); //set jsoncharacters variable to the stringified characterMap let jsoncharacters = JSON.stringify(characterMap);
Next, we will write our Apex class:
public with sharing class createSentence { @AuraEnabled public static List<Sentence__c> insertSentence(String JSONpalindrome, String JSONcharacters){ //deserialize JSON from palindromicSentences.js into Map<String, Boolean>() Map<String, Boolean> sentenceMap = (Map<String, Boolean>) JSON.deserializeStrict(JSONpalindrome, Map<String, Boolean>.class); //deserialize JSON from palindromicSentences.js into Map<String, Map<String, String>() Map<String, Map<String, String>> characterMap = (Map<String, Map<String, String>>) JSON.deserializeStrict(JSONcharacters, Map<String, Map<String, String>>.class); //create storage list to reduce DML operations List<Sentence__c> newSentences = new List<Sentence__c>(); //iterate through sentenceMap via key string for(String keySentence : sentenceMap.keySet()) { //create new Sentence__c object based on specified key value pair Sentence__c newSent = new Sentence__c(); //assignation of values newSent.Sentence__c = keySentence; newSent.IsPalindrome__c = Boolean.valueOf(sentenceMap.get(keySentence)); //ternary operator null checks to assign numerical values to letter fields newSent.A__c = characterMap.get(keySentence).get('a') != null ? Integer.valueOf(characterMap.get(keySentence).get('a')) : 0; newSent.B__c = characterMap.get(keySentence).get('b') != null ? Integer.valueOf(characterMap.get(keySentence).get('b')) : 0; newSent.C__c = characterMap.get(keySentence).get('c') != null ? Integer.valueOf(characterMap.get(keySentence).get('c')) : 0; newSent.D__c = characterMap.get(keySentence).get('d') != null ? Integer.valueOf(characterMap.get(keySentence).get('d')) : 0; newSent.E__c = characterMap.get(keySentence).get('e') != null ? Integer.valueOf(characterMap.get(keySentence).get('e')) : 0; newSent.F__c = characterMap.get(keySentence).get('f') != null ? Integer.valueOf(characterMap.get(keySentence).get('f')) : 0; newSent.G__c = characterMap.get(keySentence).get('g') != null ? Integer.valueOf(characterMap.get(keySentence).get('g')) : 0; newSent.H__c = characterMap.get(keySentence).get('h') != null ? Integer.valueOf(characterMap.get(keySentence).get('h')) : 0; newSent.I__c = characterMap.get(keySentence).get('i') != null ? Integer.valueOf(characterMap.get(keySentence).get('i')) : 0; newSent.J__c = characterMap.get(keySentence).get('j') != null ? Integer.valueOf(characterMap.get(keySentence).get('j')) : 0; newSent.K__c = characterMap.get(keySentence).get('k') != null ? Integer.valueOf(characterMap.get(keySentence).get('k')) : 0; newSent.L__c = characterMap.get(keySentence).get('l') != null ? Integer.valueOf(characterMap.get(keySentence).get('l')) : 0; newSent.M__c = characterMap.get(keySentence).get('m') != null ? Integer.valueOf(characterMap.get(keySentence).get('m')) : 0; newSent.N__c = characterMap.get(keySentence).get('n') != null ? Integer.valueOf(characterMap.get(keySentence).get('n')) : 0; newSent.O__c = characterMap.get(keySentence).get('o') != null ? Integer.valueOf(characterMap.get(keySentence).get('o')) : 0; newSent.P__c = characterMap.get(keySentence).get('p') != null ? Integer.valueOf(characterMap.get(keySentence).get('p')) : 0; newSent.Q__c = characterMap.get(keySentence).get('q') != null ? Integer.valueOf(characterMap.get(keySentence).get('q')) : 0; newSent.R__c = characterMap.get(keySentence).get('r') != null ? Integer.valueOf(characterMap.get(keySentence).get('r')) : 0; newSent.S__c = characterMap.get(keySentence).get('s') != null ? Integer.valueOf(characterMap.get(keySentence).get('s')) : 0; newSent.T__c = characterMap.get(keySentence).get('t') != null ? Integer.valueOf(characterMap.get(keySentence).get('t')) : 0; newSent.U__c = characterMap.get(keySentence).get('u') != null ? Integer.valueOf(characterMap.get(keySentence).get('u')) : 0; newSent.V__c = characterMap.get(keySentence).get('v') != null ? Integer.valueOf(characterMap.get(keySentence).get('v')) : 0; newSent.W__c = characterMap.get(keySentence).get('w') != null ? Integer.valueOf(characterMap.get(keySentence).get('w')) : 0; newSent.X__c = characterMap.get(keySentence).get('x') != null ? Integer.valueOf(characterMap.get(keySentence).get('x')) : 0; newSent.Y__c = characterMap.get(keySentence).get('y') != null ? Integer.valueOf(characterMap.get(keySentence).get('y')) : 0; newSent.Z__c = characterMap.get(keySentence).get('z') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X0__c = characterMap.get(keySentence).get('0') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X1__c = characterMap.get(keySentence).get('1') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X2__c = characterMap.get(keySentence).get('2') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X3__c = characterMap.get(keySentence).get('3') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X4__c = characterMap.get(keySentence).get('4') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X5__c = characterMap.get(keySentence).get('5') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X6__c = characterMap.get(keySentence).get('6') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X7__c = characterMap.get(keySentence).get('7') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X8__c = characterMap.get(keySentence).get('8') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; newSent.X9__c = characterMap.get(keySentence).get('9') != null ? Integer.valueOf(characterMap.get(keySentence).get('z')) : 0; //add new Sentence__c object to storage list newSentences.add(newSent); } //insert storage list newSentences using Database.SaveResult so that //the Ids of the new objects can be pulled and sent back to JS file Database.SaveResult[] srList = Database.insert(newSentences, false); //create storage List<Id> new Ids to store the Ids of new Sentence__c objects List<Id> newIds = new List<Id>(); //iterate over Database.SaveResult list for(Database.SaveResult sr : srList) { if(sr.isSuccess()) { newIds.add(sr.getId()); } } //query for the new Sentence__c objects to return back to JS file List<Sentence__c> returnSentences = [SELECT Id, Name, Sentence__c, IsPalindrome__c, A__c, B__c, C__c, D__c, E__c, F__c, G__c, H__c, I__c, J__c, K__c, L__c, M__c, N__c, O__c, P__c, Q__c, R__c, S__c, T__c, U__c, V__c, W__c, X__c, Y__c, Z__c, x0__c, x1__c, x2__c, x3__c, x4__c, x5__c, x6__c, x7__c, x8__c, x9__c FROM Sentence__c WHERE Id IN :newIds]; //return return returnSentences; } }
Lastly, we import our Apex class by adding it to the top of our JavaScript file:
import insertSentence from '@salesforce/apex/createSentence.insertSentence';
And then add our imperative Apex call into the end of our JavaScript file by adding the following:
//imperative apex call insertSentence({JSONpalindrome : jsonpalindrome, JSONcharacters : jsoncharacters}) .then(result => { //set sentence to the object that is returned this.sentence = result[0]; //get the Id of the sentence that was created to pass to nested component in part two this.sentenceId = result[0].Id; this.sentenceLoaded = true; }) .catch(error => { this.error = error; });
Now, let’s deploy to our org and try it out.


Now that we have our basic input LWC and underlying structure, we are ready to build our charts. Part two in the series coming soon.
Thanks for reading, let me know if you have any comments or questions!

Evelyn Grizzle
Another Salesforce Blog

Make a one-time donation
Make a monthly donation
Make a yearly donation
Choose an amount
Or enter a custom amount
Help keep Another Salesforce Blog on the internet by donating today!
Your contribution is appreciated.
Your contribution is appreciated.
DonateDonate monthlyDonate yearly
One response to “Creating Dynamic Charts with Chart.JS – Part One: Palindromic Sentences”
[…] the previous post, we started on our Lightning Web Component. We are going to use Chart.js to create a truly dynamic […]