Voting Component on records in Salesforce: A Guide (LWC)
I saw this question on Trailhead and thought it was quite thought-provoking. The user asked if there were any options to implement 'Voting' on objects in Salesforce, similar to how voting is handled on Salesforce's Idea Exchange (free plug to an idea I want more upvotes on - https://ideas.salesforce.com/s/idea/a0B8W00000Gdb7OUAR/criteria-based-sharing-rules-allow-lookupformuladynamic-values-and-user-field)
So let's make it! Here is an example of the voting screen offered on the idea I linked above. This voting component offers the following functionality:
- I can upvote a post
- Upvoting it causes the Upvote button to appear green, even after a refresh event.
- Upvoting it causes the 'points' view to increase by 10 (each vote is worth 10 points).
- I can 'remove' an upvote or downvote
- 'Removing' a vote returns both buttons to their neutral colors, and removes the points associated with the vote
- I can downvote a post
- Downvoting causes the downvote button to appear in red, and causes the points to drop by 10
- Total votes are tallied against each user with a vote
- Removing a vote (neutral) removes me from the count of 'Votes'
- Votes cause real-time updates to score, and score is not influenced by other voters until a refresh occurs
- This is common on other apps too, like YouTube. If I were to 'like' a video at 22 upvotes and see it suddenly jump to 35 upvotes, I might be confused. In the same way on Salesforce, I only can influence the score up or down from my user for the duration of time before I refresh.
Some really neat stuff, but how close can we get with a moderate amount of effort? Some things we will need for our solution.
Solution:
We will need to:
- Create an object that has a Master-Detail relationship with the object we are voting on, as well as a lookup to the User object
- Create 2 Rollup summary fields on the object to track score and vote counts
- Create a Lightning Web Component or Flow to display voting information and allow for votes to be cast
Option 1: LWC
Alright, let's get to it!
Step 1: Creating the object: I created an object called Opportunity_Voting__c (as I ran my voting off of the Opportunity object). Name yours whatever you'd like to! Just make sure to:
- Create an MD relation to the object you are looking to vote on
- Create a lookup to the User object
- Create a Type__c lookup
- I gave mine the values Upvote, Neutral, and Downvote
- Custom formula field (Points__c, of type Number)
- I am using the formula to pull the points off of the Type field like so
- IF(TEXT(Type__c)='Neutral',0,IF(TEXT(Type__c)='Upvote',10,-10))
- I am using the formula to pull the points off of the Type field like so
On the object you are voting on (for me this is the Opportunity), create the following fields:
- Rollup Summary field: SUM of Points
That's all for object and field creation. Now let's get into the fun stuff, the code!
Step 2: Creating a Lightning Web Component
First, I had to create a base that looks something like the Card component from the ideas site, here is what mine looks like:
Before I give the JS code, here is what this component does:
- On load, retrieves:
- The current score of the record you are on (via Score__c, the rollup summary field)
- The user's previous vote, if there was one
- If the user has previously voted, the most recent vote is captured and used to automatically fill the button.
- On vote:
- Checks where the vote came from, and what the previous button config is (effectively, for all use cases, like Upvoting a previously neutral vote, or Downvoting a previously Upvoted record. Also, you can remove votes and return to Neutral)
- Immediately updates onscreen
- Calls out to Apex, figures out if there is already a voting object for this user/record id combination
- If not, we create a new vote record
- If it finds one, we update the original vote record
- Determines the correct statuses for the buttons
Here's the code:
Cool! Now let's check out the Apex. I probably (and definitely should have) utilized @wire and some other quicker ways to reach out to Apex, but I'll leave that as an optimization for a later date 🙃
And that's really it! I also exposed the component and allowed it for record pages:
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
</targets>
Here are some screenshots:
Option 2: Flow
Using the exact same object model, let's do it in Flow (I'm not going to make it look as cool over there though).
Essentially, the idea is to:
- Get a recordId from the page
- Get the Opportunity (to pull the score)
- I don't want to use $Record here as I need to re-poll for the Opportunity data anyway.
- Get any Opportunity_Vote__c records that are already created for the user (to know where to put the default radio button value)
- Show a screen with the score on it, allow users to vote via radio button
- Run logic to either:
- Update an existing Vote object if one is found for the pair of Opp/User with the value from the radio buttons
- Create a new Vote object if we don't find one with the value of the radio buttons
- Loop back to the Opp to pull the new score, and use the (now 100% created) Vote to set the default value on the radio button
So there are two ways to essentially vote on records in your Org! Works pretty well from my testing, and can be an impressive bit of custom functionality when deployed in the right spot. It would be really easy to add a user count to this as well by adding another rollup summary field to the object you are voting on that counts the number of votes that are NOT neutral.