In the fall of 2018, I decided to replace an application my mother-in-law was using for her real estate business. I decided to replace the application with an Angular client and Spring Boot service running inside of AWS. The biggest lesson I learned was that I felt like I spent more time trying to understand AWS and less time making enhancements to her application.
That all changed when spring 2020 arrived…
While the entire world was trying to figure out how to navigate amid a global pandemic, I was diving into the Heroku PaaS… and loving it. Within a very short period of time, I was able to move this same application over to Heroku. While I know that Heroku is using AWS behind the scenes, it has had zero impact on my time. I can stay focused on what I truly enjoy: adding value to custom applications and deploying new releases using basic git-based operations.
Though I understand Heroku is not an ideal choice in every situation, a large majority of the applications running today could take advantage of what Heroku has to offer and keep feature teams supporting those applications laser-focused on adding new functionality.
As I’ve started to do more work in the mobile app space, I wondered if there is a Heroku-like option for mobile app development for building Salesforce apps. I decided to take a look first at what the Salesforce Engineering team had to offer.
The Salesforce Lightning App Builder is a point-and-click tool to create single-page applications that can be used on mobile devices. Like what Heroku offers from an application hosting perspective, the Lightning App Builder product handles a majority of the application foundation. This allows developers to focus their time on meeting business needs and objectives.
The Lightning App Builder supports single-page applications that fall into one of three categories:
For this article, I will focus on the App option and create a brand new application.
In the three decades that I have been creating applications, I have been a member of more than my fair share of feature teams. During that time, I have found joy in hearing creative developers provide witty responses to the ever-popular team manager question, “Is it done yet?”
While agile teams don’t typically ask the question the same way that traditional project managers would, the need to communicate your current status is still part of the daily regimen for most feature developers. To help developers keep things fresh, we will create the “Stand Up” app using the Lightning App Builder.
Consider these high-level requirements:
Create a custom object (called Stand Up) to house the following information:
Name (existing property) will be the response
Type includes the following values to assign a type to the response:
Create a Stand Up component and display four buttons:
The basic flow of the app:
The App will run inside the Salesforce mobile client.
With
SFDX: Create Project
To make things quick and easy, I selected the Standard option:
I called my project StandUpLightningWebComponent:
I placed the project into a folder called stand-up-web-component and opened a new workspace in VS Code:
Next, we need to connect to a Salesforce org, which I will use one of my existing sandboxes. If you need to create a new one, then check out the following URL:
Once you have a developer org to use, you can then use the following Cmd+Shift+P/Ctrl+Shift+P command to connect VS Code to that org:
SFDX: Authorize an Org
VS code will prompt for a login URL option. I selected the Project Default option:
When asked for a login alias, I simply hit the Enter key:
A browser window appeared, and I logged in to my sandbox org. The following dialog appeared:
VS Code is now connected to my sandbox, and we are ready to get started with the Stand Up component.
Using the browser page that opened in the section above, I used the Salesforce Setup perspective to create a new custom object. If you happened to close that browser window, use the following VS Code Cmd+Shift+P/Ctrl+Shift+P command to open the default org in a browser window:
SFDX: Open Default Org
WithinSetup, I simply navigated to Objects and Fields | Object Manager. Next, I used the Create | Custom Object option:
I created a new custom object with a label of Stand Up and kept the default object name of Stand_Up. However, I did replace the default Record Name value with Witty Response:
I left default values for everything else, and I clicked the Save button to establish the new object.
In order to create the Type property, I navigated to the Fields & Relationships option and clicked the New button. For this field, I chose the Picklist option and clicked the Next button, which led to the following screen:
The picklist will be limited to the three choices noted above. I clicked the Next button twice (to accept these values and the default field security), and then clicked the Save button to add the new field. The Stand Up object is ready for use:
Next, I used the Integrations | Data Import Wizard process in Salesforce to import the following records from the sample-data.csv
file into the Stand Upcustom object.
All is good in the hood!,Positive
I am free and need something to work on.,Positive
Should be finished today.,Positive
Been battling a migraine - no update.,Neutral
It works but it hasn't been tested,Neutral
QA is testing things right now - fingers crossed.,Neutral
Still making progress - but not finished.,Neutral
Waiting for my merge-request to be approved.,Neutral
I am honestly thinking about a career change.,Negative
It is as done as it is gonna be - is that good enough?,Negative
My dog ate my code and I am starting over.,Negative
If you are unfamiliar with the process, you can read about how to use the import wizard here:
Once the import was completed, I create a file called Stand_Up__c.soql file inside the Scripts | soql section of VS Code, which contained the following contents:
// Use .soql files to store SOQL queries.
// You can execute queries in VS Code by selecting the
// query text and running the command:
// SFDX: Execute SOQL Query with Currently Selected Text
SELECT Id, Name, Type__c FROM Stand_Up__c ORDER BY Name ASC
Next, I highlighted the SELECT statement and used the Cmd+Shift+P/Ctrl+Shift+P command to select the following option:
SFDX: Execute SOQL Query with Currently Selected Text
This action produced the following results in my Terminal tab:
Starting SFDX: Execute SOQL Query...
12:29:51.99 sfdx force:data:soql:query --query SELECT Id, Name, Type__c FROM Stand_Up__c ORDER BY Name ASC
Querying Data... done
ID NAME TYPE__C
────────────────── ────────────────────────────────────────────────────── ────────
a092L00000E6eX4QAJ Been battling a migraine - no update. Neutral
a092L00000E6eX2QAJ I am free and need something to work on. Positive
a092L00000E6eX9QAJ I am honestly thinking about a career change. Negative
a092L00000E6eXAQAZ It is as done as it is gonna be - is that good enough? Negative
a092L00000E6eX5QAJ It works but it hasn't been tested Neutral
a092L00000E6eXBQAZ My dog ate my code and I am starting over. Negative
a092L00000E6eX6QAJ QA is testing things right now - fingers crossed. Neutral
a092L00000E6eX3QAJ Should be finished today. Positive
a092L00000E6eX7QAJ Still making progress - but not finished. Neutral
a092L00000E6eX8QAJ Waiting for my merge-request to be approved. Neutral
Total number of records retrieved: 10.
12:29:52.681 sfdx force:data:soql:query --query SELECT Id, Name, Type__c FROM Stand_Up__c ORDER BY Name ASC
ended with exit code 0
Finally, I wanted to pull all the metadata for the custom object into VS Code. This way, the custom object can be included in the git-based repository, eliminating the need for component users to create the object manually.
To import the Stand Up custom object into VS Code, use the following command from the Terminal window:
sfdx force:source:retrieve -m CustomObject:Stand_Up__c
The following output appeared in VS Code:
╭─jv@me ~/projects/jvc/stand-up-web-component/StandUpLightningWebComponent
╰─$ sfdx force:source:retrieve -m CustomObject:Stand_Up__c
Preparing retrieve request... done
=== Retrieved Source
FULL NAME TYPE PROJECT PATH
────────────────── ──────────── ────────────────────────────────────────────────────────────────────────
Stand_Up__c.Type__c CustomField force-app/main/default/objects/Stand_Up__c/fields/Type__c.field-meta.xml
Stand_Up__c CustomObject force-app/main/default/objects/Stand_Up__c/Stand_Up__c.object-meta.xml
It is also possible to achieve the same result in VS Code by clicking the cloud icon in the toolbar, locating the items you wish to retrieve and pushing the download icon.
On the left-hand side of VS code, the Stand Up custom object is now available:
With the object and data ready, we can build the Lightning Web Component which will be used with the app.
We will create the Stand Up controller first, which will act as an API to locate the appropriate response.
We can use the following Cmd+Shift+P/Ctrl+Shift+P command to create the StandUpController class:
SFDX: Create Apex Class
I populated the simple controller as noted below:
public with sharing class StandUpController {
@AuraEnabled(cacheable=false)
public static Stand_Up__c getRandomResponse(String responseType) {
List<Stand_Up__c> responseList;
if (responseType.equals('all')) {
responseList = [SELECT Name, Type__c FROM Stand_Up__c];
} else {
responseList = [SELECT Name, Type__c FROM Stand_Up__c WHERE Type__c = :responseType];
}
return getRandomFromList(responseList);
}
private static Stand_Up__c getRandomFromList(List<Stand_Up__c> responseList) {
if (responseList == null || responseList.isEmpty()) {
Stand_Up__c standUpResponse = new Stand_Up__c();
standUpResponse.Name = 'Ummmm.......';
standUpResponse.Type__c = 'Neutral';
return standUpResponse;
}
Integer count = responseList.size();
Integer rand = Math.floor(Math.random() * count).intValue();
return responseList[rand];
}
}
The client will call the StandUpController.getRandomResponse()
method, passing in the appropriate responseType
option, which will return a Stand_Up__c
record. If there are no records available to match the request, then a new Stand_Up__c
object will be returned with the neutral status of simply “Ummmm.......”
We are now ready to create the Lightning Web Component for the Stand Up app.
We can use the following Cmd+Shift+P/Ctrl+Shift+P command to create the standUp Lightning Web component:
SFDX: Create Lightning Web Component
The first thing we need to do is update thestandUp.js-meta.xml
to make this component available for use:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="standUp">
<apiVersion>53.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
Next, the standUp.js
needs to be updated to process requests by the app, interacting with the controller we created in the last section:
import { LightningElement } from 'lwc';
import getRandomResponse from '@salesforce/apex/StandUpController.getRandomResponse';
export default class StandUp extends LightningElement {
standUpResponse = null;
error = null;
getPositiveResponse() {
this.getRandomResponseFromSalesforce('Positive');
}
getNeutralResponse() {
this.getRandomResponseFromSalesforce('Neutral');
}
getNegativeResponse() {
this.getRandomResponseFromSalesforce('Negative');
}
getAnyResponse() {
this.getRandomResponseFromSalesforce('all');
}
getRandomResponseFromSalesforce(responseType) {
this.standUpResponse = null;
this.error = null;
getRandomResponse({responseType})
.then((standUp) => {
this.standUpResponse = standUp.Name;
})
.catch((error) => {
this.error = JSON.stringify(error);
});
}
}
Finally, we need to update the standUp.html
file to include the view-layer logic:
<template>
<div class="slds-var-m-vertical_large slds-var-p-vertical_medium
slds-text-align_center slds-border_top slds-border_bottom slds-text-heading_small">
<p>Select the type of update you wish to find: </p>
</div>
<div class="slds-text-align_center slds-var-p-top_xx-small">
<lightning-button variant="brand" class="slds-var-m-left_x-small"
icon-name="utility:cases" label="Positive" title="Positive"
onclick={getPositiveResponse}>
</lightning-button>
</div>
<div class="slds-text-align_center slds-var-p-top_xx-small">
<lightning-button variant="brand" class="slds-var-m-left_x-small"
icon-name="utility:cases" label="Neutral" title="Neutral"
onclick={getNeutralResponse}>
</lightning-button>
</div>
<div class="slds-text-align_center slds-var-p-top_xx-small">
<lightning-button variant="brand" class="slds-var-m-left_x-small"
icon-name="utility:cases" label="Negative" title="Negative"
onclick={getNegativeResponse}>
</lightning-button>
</div>
<div class="slds-text-align_center slds-var-p-top_xx-small">
<lightning-button variant="brand" class="slds-var-m-left_x-small"
icon-name="utility:cases" label="Any" title="Any"
onclick={getAnyResponse}>
</lightning-button>
</div>
<div if:true={standUpResponse} class="slds-var-m-vertical_large slds-var-p-vertical_medium
slds-text-align_center slds-border_top slds-border_bottom slds-text-heading_small">
<p>Today's Update: {standUpResponse}</p>
</div>
<div if:true={error} class="slds-var-m-vertical_large slds-var-p-vertical_medium
slds-text-align_center slds-border_top slds-border_bottom slds-text-color_error">
<p>Error: {error}</p>
</div>
</template>
Now, we are ready to deploy the Stand Up app to Salesforce.
Pushing all my code from the local machine to Salesforce is simple. All I need to do is right click on the force-app/main/default in the navigator and select the SFDX: Deploy Source to Org option:
Once completed, the following dialog will appear in VS Code:
Now we are ready to add the Lightning Web Component to a new app in Salesforce.
I switched over to the browser tab logged into my Salesforce org and opened the Setup perspective. Next, I navigated to the Apps | App Manager page and clicked the New Lightning App button.
I decided to call the new app Stand Up and even found a nice little icon to use. I used the rest of the default settings from the wizard, except the last screen, where I granted all users access to this app.
Then, I navigated to theUser Interface | Lightning App Builder screen. Here, I created a new Lightning App Page called Stand Up, which was designed as an App Page with a single region.
On the left side of the screen, I could see my standUp LWC under the Custom section. All I had to do was drag that component over and drop it into the single region for the Lightning-based page.
After saving the component, I used the activation process to expose the Lightning page for clients to utilize.
During the activation phase, I set the App Name toStand Up and found the best icon on the list:
To set up the Lightning Experience, I dropped the Stand Up app onto the right side of the screen:
For the Mobile Navigation, I added the Stand Up Lighting app and made sure it was near the top of the list:
After hitting the Save button, the Stand Up app was ready for use.
After launching the Salesforce mobile app, I was able to see the Stand Up app I just created:
Next, I tapped the Stand Up application, which displayed the following screen with my custom LWC:
Tapping the Any button will return a random response from the Stand_Up__c
record set:
The app is also available for use in Salesforce, without making any updates to the code:
Starting in 2021, I have been trying to live by the following mission statement, which I feel can apply to any IT professional:
“Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”
- J. Vester
Just like I found with my evaluation of the Heroku platform, I was able to quickly introduce a Salesforce mobile app by leveraging the Salesforce platform. In doing so, I remained focused on meeting the needs of the application rather than worrying about foundational and design decisions.
Truly, this pattern adheres to my mission statement … and the time required to complete this example was measured in minutes instead of hours or even days.
If you are interested in the source code for this article, simply navigate to the following repository on GitLab:
Have a really great day!