Display barcode in Salesforce record detail view as a button

Here we cover an easy way to display QR code in Salesforce classic using a free appexchange app Barcode and QR Code Generator.

This app allows you to generate barcode in Salesforce without depending on any external services. In below step by step guide, we are assuming that you have installed the app already. If not please go to Barcode and QR Code Generator and install.


Example - Displaying QR code using custom field in custom object

Here we will go through an example of displaying QR code using data from a custom field. Here for demo purpose I have created a custom object with name Demo_Record__c. This object has a custom field with API name Unique_Field__c. This is just one example. Same approach will work for any other field in any custom/standard object in Salesforce.


1) Create a barcode config record for the custom object and field combination

Go to "Barcode Configs" tab that comes with the app and create a new record. Give it a name without spaces or special characters. I am giving it a name DemoUniqueFieldConfig. Specify Demo_Record__c as object name and Unique_Field__c as field name. Then in order to display QR code, I am selecting "QR Code" from "Barcode type" picklist/dropdown field. You can leave other fields with default values. Then save the record. Please check below screenshot,

2) Create a custom button to display QR Code in "Demo Record" page layout

Navigate to "Setup => Objects => Demo Record". Scroll down to "Buttons, Links, and Actions" section and create a new button.

Generally to display barcode in a new widow you can just redirect user to below URL pattern /apex/BarQR__BarcodeGeneratorPage?configName=YOUR_CONFIG_RECORD_NAME_HERE&recordId=RECORD_ID_HERE

We are doing the same here in the button. We are using the configuration record name DemoUniqueFieldConfig from Step 1 and passing recordId dynamically using {!Demo_Record__c.Id}.

Save your button code. At the end you can add the button to page layout under "Setup => Objects => Demo Record => Page Layouts => Edit". Check below screenshot.

Click save and navigate to a record details page. You will be able to see "QR Code" button in the page. When you click on the button, QR code will be displayed in a popup window using data from the field you configured.

Result

Example from Salesforce classic view.

Since our "QR Code" button is a custom button that redirects the user to a page with barcode, it will be available in lightning experience also. But in lightning Barcode/QR code will be more easily accessible if you add it through app builder page. Checkout example of adding barcode component directly in lightning app builder page


Use cases

Barcode generation and scanning can be used to improve a lot of business processes. Some examples are,

  • Generate product barcodes and stick labels to products so that you can scan and add products to opportunity
  • Generate QR code corresponding to your event registration custom object field(Id or registration number) and email users, so that you can scan in participants at the time of the event
  • Stick a QR code in your outgoing shipments and incoming shipments and then scan it into Salesforce records so that you can track shippings end to end

Displaying barcode in Salesforce lightning experience natively

Here we cover an easy way to display barcode in lightning experience using a free appexchange app Barcode and QR Code Generator.

This app allows you to generate barcode in Salesforce without depending on any external services. In below example, we are assuming that you have the app installed already. If not please go to Barcode and QR Code Generator and install.


Example - Displaying barcode using email field in lead

Here we will go through an example of displaying barcode using data from email field in lead records. This is just one example. Same approach will work for any other field in any custom/standard object in Salesforce.


1) Create a barcode config record for lead email field

Go to "Barcode Configs" tab that comes with the app and create a new record. Give it a name without spaces or special characters. Specify lead as object name and email as field name. You can leave other fields with default values. Then save the record. Please check below screenshot,

2) Add Barcode component to lead flexipage and activate

Once you have created a barcode config record, it is just matter of editing lead flexipage and adding "Barcode and QR Code Generator" component to the page.

First go to a lead record details page. Click Gear Icon and "Edit Page".

Drop "Barcode and QR Code Generator" component from left sidebar to the place you need. It will ask for a configName. Give the name of the record you created from step 1.

Click "Save" button on top right and finally click "Activation" on top right and assign the page as organization default (or based on your app or profile preferences).


Result

If you followed the steps correctly, you should see barcode when you go to any lead record page.


Use cases

Barcode generation and scanning can be used to improve a lot of business processes. Some examples are,

  • Generate product barcodes and stick labels to products so that you can scan and add products to opportunity
  • Generate QR code corresponding to your event registration custom object field(Id or registration number) and email users, so that you can scan in participants at the time of the event
  • Stick a QR code in your outgoing shipments and incoming shipments and then scan it into Salesforce records so that you can track shippings end to end
  • Need to scan the barcode values to Salesforce as well? Check out the article here

Long-Running callout with continuation in Lightning

API callouts that take more than 5 seconds are called long-running callouts. Salesforce allows only 10 long-running callouts at a time in an environment. To avoid hitting this limit Salesforce provides continuation. This uses a callback mechanism and can be implemented in Visualforce or Aura.

Use cases

If you have an integration that is used by more than 10 concurrent users and the API can take more than 5 seconds to respond, you should consider using continuation.

Steps to Implement

STEP 1 :- Create a remote site setting/named credential corresponding to the API you want to access. You can find remote site settings under setup => remote site settings. To follow this example, create a remote site setting whitelisting the domain https://salesforcecodes.blogspot.com/

STEP 2 :- Create an apex class that has an AuraEnabled method and annotated as continuation=true. This method starts the continuation processing. Add a second method to process the result. It is processBlogResponse in our example.

public with sharing class ContinuationDemo {
@AuraEnabled(continuation=true cacheable=true)
public static Object startContinuationRequest() {
Integer timeout = 50;
Continuation con = new Continuation(timeout);
//Apex method to execute after getting response
con.continuationMethod='processBlogResponse';
//State can be a variable of any type supported in apex
con.state='Additional variable to give context. If more than one use list/set/map';
//Create Http Request
HttpRequest req = new HttpRequest();
req.setMethod('GET');
//Make sure that your endpoint is added in Remote site settings
req.setEndpoint('https://salesforcecodes.blogspot.com/feeds/posts/default?max-results=10&alt=json');
//Add request to continuation. Can add up to a maximum of three requests
con.addHttpRequest(req);
return con;
}
@AuraEnabled(cacheable=true)
public static String processBlogResponse(List<String> labels, Object state) {
// One label will be generated per request in continuation
HttpResponse response = Continuation.getResponse(labels[0]);
return response.getBody();
}
}

STEP 3 :- Create an aura component and call the apex method annotated with continuation=true. Syntax to call continuation method from aura is similar to calling other apex methods from aura.

<aura:component implements="flexipage:availableForAllPageTypes" controller="ContinuationDemo">
<aura:handler name="init" value="{!this}" action="{!c.handleInit}" />
<aura:attribute name="blogs" type="List" default="[]" />
<aura:iteration items="{!v.blogs}" var="blog">
<lightning:card title="{!blog.title}">
<p class="slds-p-horizontal_small">
<a href="{!blog.url}" target="_blank">{!blog.title}</a>
</p>
</lightning:card>
</aura:iteration>
</aura:component>
({
handleInit : function(component, event, helper) {
var action = component.get('c.startContinuationRequest');
action.setCallback(this, function(response) {
if (component.isValid && response.getState().toLowerCase() === 'success') {
let responseObj = JSON.parse(response.getReturnValue());
let blogs = responseObj.feed.entry.map(blogEntry => ({
"title": blogEntry.title.$t,
"url": blogEntry.link[4].href
}));
component.set('v.blogs', blogs);
} else {
console.log(response.getError()[0].message);
}
});
$A.enqueueAction(action);
}
})

STEP 4 :- Create an aura app or flexi page to display your aura component with continuation. In this example I am creating a TestApp aura applicaiton. After this go to https://YOURDOMAINHERE.com/c/TestApp.app to see the application displaying blogs retrieved using continuation.

<aura:application extends="force:slds">
<div>
<c:DisplayBlogs></c:DisplayBlogs>
</div>
</aura:application>
view raw Testapp.app hosted with ❤ by GitHub

How to authenticate to Salesforce APIs without client Id and secret

There are multiple ways to authenticate to Salesforce SOAP and REST APIs. Usually the recommended way is to authenticate to REST API is by using OAuth and creating a connected app in Salesforce. Please check the article Using curl to authenticate to Salesforce REST API to see an example.

There is an easy way to bypass OAuth and directly use username+password combination to authenticate against Salesforce SOAP and REST APIs. It is by making SOAP login call. It will return an session Id that you can use against Salesforce SOAP and REST APIs as access token.

Getting access token with username, password and security token

You need make a HTTP call using below format. This is basically sending a login SOAP request. Make sure to use your username, password and security token in line 12 and 13. Also if you are accessing a Salesforce sandbox base part of the URL changes to https://test.salesforce.com

#Use below API call to make SOAP login call. It will return a session token
curl --location --request POST 'https://login.salesforce.com/services/Soap/u/49.0' \
--header 'SOAPAction: login' \
--header 'Content-Type: text/xml' \
--header 'charset: UTF-8' \
--data-raw '<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<n1:login xmlns:n1="urn:partner.soap.sforce.com">
<n1:username>USERNAMEHERE</n1:username>
<n1:password>PASSWORD+SECURITYTOKEN</n1:password>
</n1:login>
</env:Body>
</env:Envelope>'
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="urn:partner.soap.sforce.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<loginResponse>
<result>
<metadataServerUrl>https://salesforcecodestest-dev-ed.my.salesforce.com/services/Soap/m/49.0/00D3h000007NZNL</metadataServerUrl>
<passwordExpired>false</passwordExpired>
<sandbox>false</sandbox>
<serverUrl>https://salesforcecodestest-dev-ed.my.salesforce.com/services/Soap/u/49.0/00D3h000007NZNL</serverUrl>
<sessionId>00D3h000007NZNL!ARcAQLOeKShbULku1z4e9_2J6kKPUO.GStIgWzrxz2rpMSzUIykkVqDQ5YIapg_Iz0fxX7XlVXVHRjLESJPNdsX.tmxK83H</sessionId>
<userId>0053h0000031gxzAAA</userId>
<userInfo>
<accessibilityMode>false</accessibilityMode>
<chatterExternal>false</chatterExternal>
<currencySymbol>$</currencySymbol>
<orgAttachmentFileSizeLimit>26214400</orgAttachmentFileSizeLimit>
<orgDefaultCurrencyIsoCode>USD</orgDefaultCurrencyIsoCode>
<orgDefaultCurrencyLocale>en_US</orgDefaultCurrencyLocale>
<orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
<orgHasPersonAccounts>false</orgHasPersonAccounts>
<organizationId>00D3h000007NZILEA4</organizationId>
<organizationMultiCurrency>false</organizationMultiCurrency>
<organizationName>SalesforceCodes</organizationName>
<profileId>00e3h000001hnwxAAA</profileId>
<roleId xsi:nil="true"/>
<sessionSecondsValid>7200</sessionSecondsValid>
<userDefaultCurrencyIsoCode xsi:nil="true"/>
<userEmail>salesforcecodestest@gmail.com</userEmail>
<userFullName>SalesforceCodes</userFullName>
<userId>0053h0000031gxzAAA</userId>
<userLanguage>en_US</userLanguage>
<userLocale>en_US</userLocale>
<userName>salesforcecodestest@grace.com</userName>
<userTimeZone>America/Los_Angeles</userTimeZone>
<userType>Standard</userType>
<userUiSkin>Theme3</userUiSkin>
</userInfo>
</result>
</loginResponse>
</soapenv:Body>
</soapenv:Envelope>

You can see that the API response contains sessionId tag. You can use this sessionId to call Salesforce SOAP or REST apis. Checkout below example of accessing Salesforce Rest API with the session Id. Make sure to use your session ID in authorization header and your base endpoint instead of https://salesforcecodestest-dev-ed.my.salesforce.com.

curl https://salesforcecodestest-dev-ed.my.salesforce.com/services/data/v48.0/query?q=SELECT+Id,+Name+FROM+Account+LIMIT+1 \
-H 'Authorization: OAuth SessionIdHere' \
-H 'X-PrettyPrint:1'

This method allows you to access Salesforce REST and SOAP apis without a client ID and secret. This is intended for server to server communication. If you use it in a client side app, you will see CORS errors.

Retrieving metadata from Salesforce using package.xml and SFDX CLI

Salesforce ecosystem provides a number of tools to retrieve metadata from connected Salesforce environment. There is an option in Salesforce extension pack for vscode to pull metdata from your connected Salesforce environment. But sometimes you still need to pull metadata from unreleated environments for purposes like committing to a git repository or deploying.

Metadata type names can be tricky and it usually difficult to create a package.xml without some googling. Please checkout article on How to create package.xml in Salesforce - With Samples

Retrieving metadata using package.xml

Once you have package.xml, you are only couple of commands away from retrieving metadata with it. Please follow below steps,

  1. Make sure you have SFDX CLI tool installed. If not download and install it from SFDX CLI Downlaod
  2. Make sure your CLI is connected to the environment from where you need to retrieve metadata. If not use below command to authenticate your Salesforce environment

    sfdx force:auth:web:login -r "https://yourSalesforceEnvironmentUrlHere.com" -a aliasNameHere

  3. Copy package.xml to your command line location and create a folder with name "metadata" to store the metadata and run below command

    sfdx force:mdapi:retrieve -r metadata -k ./package.xml -w 5 -u aliasNameHere

You will be able to see a progress status and SFDX should retrieve code and put in your metadata folder as a zip file


Package.xml samples to retrieve Salesforce metadata

Here we will go through how we can create a package.xml to retrieve metadata from Salesforce. Common metadata type samples are also included.

Metadata type names can be tricky and it usually difficult to create a package.xml without some googling. Here we provide a sample package.xml with examples of common types.

Once you have a package.xml, it is relatively easy to retrieve metadata using it and deploy to other environments. There are multiple options for that- Force.com Migration tool/ANT (Outdated), SFDX (Recommended)- How to retrieve code using SFDX CLI.

Package.xml sample with common metadata types

When you are creating package.xml, copy below simple package.xml file as a starting point

<!--starting template-->
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>AccountController</members>
<name>ApexClass</name>
</types>
<version>45.0</version>
</Package>
view raw package.xml hosted with ❤ by GitHub

Now copy relevant types from below xml and add to your package.xml. Then go to your org and add members tags for the elements you want to retrieve

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>AccountController</members>
<name>ApexClass</name>
</types>
<!--Visualforce sample-->
<types>
<members>AccountPage</members>
<name>ApexPage</name>
</types>
<!--Trigger sample-->
<types>
<members>AccountTrigger</members>
<name>ApexTrigger</name>
</types>
<!--Aura component sample-->
<types>
<members>MyAuraComponent</members>
<name>AuraDefinitionBundle</name>
</types>
<!--LWC component sample-->
<types>
<members>MyLwcComponent</members>
<name>LightningComponentBundle</name>
</types>
<!--Custom Field sample-->
<types>
<members>Account.CustomFieldOnAccount__c</members>
<members>CustomObject__c.CustomField__c</members>
<name>CustomField</name>
</types>
<types>
<!--Custom metadata is retrieved as object.
Then individual entries in metadatatype is retrieved
with CustomMetadata type-->
<members>Custom_Metadata_Name__mdt</members>
<members>CustomObject__c</members>
<name>CustomObject</name>
</types>
<types>
<members>Custom_Metadata_Name.First_Entry</members>
<name>CustomMetadata</name>
</types>
<types>
<members>Recruiting_App</members>
<name>CustomApplication</name>
</types>
<types>
<members>SystemError</members>
<name>CustomLabel</name>
</types>
<types>
<!--API Name of tab if vf/lighting/web tab-->
<members>Batch_Manager</members>
<!-- Object API name if custom object tab -->
<members>Custom_Object__c</members>
<name>CustomTab</name>
</types>
<types>
<members>Account.Validation_Rule_Name_Here</members>
<members>CustomObject__c.Validation_Rule_Name_Here</members>
<name>ValidationRule</name>
</types>
<!--Page Layout Sample-->
<types>
<members>CustomObject__c-Elite Customer Layout</members>
<name>Layout</name>
</types>
<!--Highlights panel layout sample-->
<types>
<members>Account.Account_Highlights</members>
<name>CompactLayout</name>
</types>
<types>
<members>AppLogo</members>
<name>ContentAsset</name>
</types>
<types>
<members>GenesysAdapter</members>
<name>CallCenter</name>
</types>
<types>
<members>Custom_Object__c.Prevent_Duplicate_Name</members>
<name>DuplicateRule</name>
</types>
<types>
<members>Custom_Object__c.Match_Name</members>
<name>MatchingRule</name>
</types>
<types>
<members>Case.ApprovalFields</members>
<name>FieldSet</name>
</types>
<types>
<members>AppBuilderPageName</members>
<name>FlexiPage</name>
</types>
<types>
<members>ProcessBuilderFlowName</members>
<name>Flow</name>
</types>
<types>
<members>EmployeeTypes</members>
<name>GlobalValueSet</name>
</types>
<types>
<members>CustomObject__c.All</members>
<name>ListView</name>
</types>
<types>
<members>Module_Admin</members>
<name>PermissionSet</name>
</types>
<types>
<members>Company Admin</members>
<name>Profile</name>
</types>
<types>
<members>Maintenance_NewCase</members>
<name>QuickAction</name>
</types>
<types>
<members>ReportFolderName/Custom_Report_Name</members>
<name>Report</name>
</types>
<types>
<members>CasesWithCalls</members>
<name>ReportType</name>
</types>
<types>
<members>CaseStatus</members>
<name>StandardValueSet</name>
</types>
<types>
<members>ChartJs</members>
<name>StaticResource</name>
</types>
<types>
<members>Case.Search_Transactions</members>
<name>WebLink</name>
</types>
<types>
<members>Case.Update_Last_Reminder_Date</members>
<name>WorkflowFieldUpdate</name>
</types>
<types>
<members>Case.Send Reminder</members>
<name>WorkflowRule</name>
</types>
<types>
<members>Case.Send_Reminder_Message</members>
<name>WorkflowTask</name>
</types>
<types>
<members>Case.Support Process</members>
<name>BusinessProcess</name>
</types>
<version>48.0</version>
</Package>

Setting up Skill based routing in Salesforce Omni-Channel

Salesforce Omni-channel feature is used to route work to agents in real time based on the routing configuration you define. You can use this feature to route cases, leads, chats, social posts, custom objects etc to the users you need.

Configuring simple Omni-channel setup to route records to agents is complex. It becomes even more complex when you are trying to route records to users/agents based on their skills. Here we outline the steps needed for simple omni-channel routing and skill based omni-channel routing

Simple Omni-Channel Routing

  1. Enable Omni Channel
    • Setup => Check "Enable Omni-Channel" checkbox
  2. Create a Service Channel for your object (can select case, lead, custom object etc)
    • Setup => Service Channel => Click "New" and create a service channel for the object you need
  3. Create Routing configuration
    • Setup => Routing Configuration => New
    • Routing Models (Least Active => Person with least number of cases, Most Available => Can give different capacity for agents - eg junior agents get half capacity)
  4. Create queues and assign routing configuration from previous step
  5. Create Presence Configuration
    • Setup => Presence Configuration => New
    • Can specify max capacity per agent
  6. Create Presence Statuses
    • Setup => Presence Status => New
    • Examples - busy, available etc
  7. Assign statuses to profile
    • Go to the profile and assign statuses to the profile
  8. Add Omni-channel component to app
    • Create/Edit any console app and add "Omni-Channel" component to the utility bar
  9. Go to "Omni Supervisor" tab and you will be able to see status of agents

End to end walkthrough ( Credits to Salesforce Exclusive channel)



Omni-Channel configuration with Skill based routing

  1. Enable Omni Channel
    • Setup => Check "Enable Omni-Channel" checkbox and "Enable Skill-based routing" checkbox
  2. Create a Service Channel for your object (can be case, lead, custom object etc)
    • Setup => Service Channel => Click "New" and create a service channel for the object you need
  3. Create Skills
    • Setup => Skills => New
    • Usually languages and product family names are used as skills
  4. Create Service Resource
    • Created from "Service Resource" tab. This basically registers users as agents who are ready to work on records
    • Service resources can be assigned with skill in "Skills" related list with optional start date and end date
  5. Route cases to agents
    • Routing using Apex
      • Create trigger/process builder that runs apex when a new case is created
      • Insert PendingServiceRouting record in IsReadyForRouting value as false
      • Create "SkillRequirement" record with skills required as junction record between PendingServiceRouting and skill. Use case description/other fields to find skills required
      • Change IsReadyForRouting value to true on PendingServiceRouting records
    • Routing using Flow
      • Create an auto launched flow that gets called on case creation
      • Use "Routing" action and pass (record Id, configuration Id and skill Ids). Salesforce will take care of routing.
      • If flow based routing is used routing configuration also needs to be created
  6. Create Presence Configuration
    • Setup => Presence Configuration => New
    • Can specify max capacity per agent
  7. Create Presence Statuses
    • Setup => Presence Status => New
    • Examples - busy, available etc
  8. Assign statuses to profile
    • Go to the profile and assign statuses to the profile
  9. Add Omni-channel component to app
    • Create/Edit any console app and add "Omni-Channel" component to the utility bar
  10. Go to "Omni Supervisor" tab and you will be able to see status of agents

End to end walkthrough ( Credits to Salesforce Exclusive channel)


Using Lightning Message Service to communicate between lightning components and visualforce

Lightning message service allows you to communicate between all UI technologies of Salesforce (LWC, Aura and Visualforce). This is the recommended way to communicate between lightning components and Visualforce.

Lighting message service communication works in below three steps

  1. Create a new MessageChannel metadata file and deploy to your org
  2. Publish a message from one of the three UI technologies
  3. Subscribe to the message in any of the three UI technologies

1) Creating new MessageChannel

Currently Salesforce do not provide any option in the UI to create MessageChannel. So you have to deploy it as metadata. isExposed setting in metadata allow you to enable communicate between different namespaces/managed packages.

Copy below code metadata to messageChannels folder in your code folder. Name the file as CarSelectionChannel.messageChannel-meta.xml


<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>CarSelectionChannel</masterLabel>
<isExposed>true</isExposed>
<description>This Lightning Message Channel is used to notify when new cars are selected.</description>
<lightningMessageFields>
<fieldName>carName</fieldName>
<description>This is the name of the newly selected car</description>
</lightningMessageFields>
</LightningMessageChannel>

If you have sfdx installed, you can use below line to deploy the file.

sfdx force:source:deploy -p "pathToFile" -u targetOrgAliasHere

2) Publish event on the MessageChannel

This step varies depending on the whether you are publishing it from LWC or Aura or Visualforce. Our example scenario outlines subscribing and publishing from all three technologies.

3) Subscribing to events on MessageChannel

This step also varies depending on the whether you are publishing it from LWC or Aura or Visualforce. Our example scenario outlines subscribing and listening from all three technologies.

Visualforce Example

Here we use sforce.one methods. This do not work as a stand alone visualforce page. Make sure you are inside lightning experience. ie create a visualforce page with below code, create a tab for that visualforce page and check from lightning experience.

<apex:page>
<style>
.green-border {
background-color: white;
border: 3px solid green;
padding: 50px;
width: 250px;
height: 250px;
float:left;
}
</style>
<div class="green-border">
<h1>Visualforce</h1>
<br/>
<p>Please select your car:</p>
<input type="radio" id="audi" name="car" value="Audi" onclick="fireEvent(this.value);"/>
<label for="audi">Audi</label><br/>
<input type="radio" id="tesla" name="car" value="Tesla" onclick="fireEvent(this.value);"/>
<label for="tesla">Tesla</label><br/>
<input type="radio" id="bmw" name="car" value="BMW" onclick="fireEvent(this.value);"/>
<label for="bmw">BMW</label>
<br/>
<br/>
<h2>Last message received</h2>
<br/>
<div id="lastPayload">
</div>
</div>
<script>
//Getting CarSelectionChannel message channel ID. Need __c postfix
let channelId = '{!$MessageChannel.CarSelectionChannel__c}';
//Publishes event with selected car name
function fireEvent(value) {
sforce.one.publish(channelId, {source: 'VF', carName: value});
}
//Subscribes and stores subscription ID to unsubscribe later if needed.
let subscriptionId;
function subscribeToMessageChannel() {
subscriptionId = sforce.one.subscribe(channelId, (data) => {
document.querySelector('#lastPayload').textContent = JSON.stringify(data);
});
}
subscribeToMessageChannel();
//Unsubscribes from subscription. Not used in the demo
function unsubscribeFromMessageChannel() {
if(subscriptionId) {
sforce.one.unsubscribe(subscriptionId);
subscriptionId = undefined;
}
}
</script>
</apex:page>

Aura Example

Here we use lightning:messageChannel in the component to subscribe to event.

<aura:component implements="flexipage:availableForAllPageTypes" access="global">
<aura:attribute name="lastMessage" type="string"/>
<lightning:messageChannel type="CarSelectionChannel__c" aura:id="carChannel" onMessage="{!c.handleCarSelectionMessage}"/>
<lightning:card >
<div class="red-border">
<h1 class="slds-text-heading_medium">Aura</h1>
<br/>
<p>Please select your car:</p>
<input type="radio" id="audi" name="car" value="Audi" onclick="{!c.publishEventInMessageChannel}"/>
<label for="audi">Audi</label><br/>
<input type="radio" id="tesla" name="car" value="Tesla" onclick="{!c.publishEventInMessageChannel}"/>
<label for="tesla">Tesla</label><br/>
<input type="radio" id="bmw" name="car" value="BMW" onclick="{!c.publishEventInMessageChannel}"/>
<label for="bmw">BMW</label>
<br/>
<br/>
<h2>Last message received</h2>
<br/>
<div id="lastPayload">
{!v.lastMessage}
</div>
</div>
</lightning:card>
</aura:component>
view raw lmsAura.cmp hosted with ❤ by GitHub
.THIS {
padding: 40px;
}
.THIS .red-border {
background-color: white;
border: 3px solid red;
padding: 50px;
width: 320px;
height: 250px;
}
view raw lmsAura.css hosted with ❤ by GitHub
({
publishEventInMessageChannel : function(component, event, helper) {
const payload = { source: "Aura", carName: event.target.value };
component.find("carChannel").publish(payload);
},
handleCarSelectionMessage: function (component, event, helper) {
const carName = event.getParam("carName");
const source = event.getParam("source");
component.set("v.lastMessage", `{source: "${source}", carName: "${carName}"}`);
}
})

LWC Example

Here we import @salesforce/messageChannel/CarSelectionChannel__c in JavaScript controller. Also we use subscribe, unsubscribe methods imported from lightning/messageService to subscribe to event.

Please note that if you are just publishing message to channel and not listening to it, you just need to import publish method and the channel. Then you can use the code in handleCarSelect method to publish message to the channel.

.container {
background-color: white;
padding:40px;
}
.red-border {
border: 3px solid blue;
padding: 50px;
width: 320px;
height: 250px;
}
view raw lmsLwc.css hosted with ❤ by GitHub
<template>
<div class="container">
<div class="red-border">
<h1 class="slds-text-heading_medium">LWC</h1>
<br/>
<p>Please select your car:</p>
<input type="radio" id="audi" name="car" value="Audi" onclick={handleCarSelect}/>
<label for="audi">Audi</label><br/>
<input type="radio" id="tesla" name="car" value="Tesla" onclick={handleCarSelect}/>
<label for="tesla">Tesla</label><br/>
<input type="radio" id="bmw" name="car" value="BMW" onclick={handleCarSelect}/>
<label for="bmw">BMW</label>
<br/>
<br/>
<h2>Last message received</h2>
<br/>
<div id="lastPayload">
{lastMessage}
</div>
</div>
</div>
</template>
view raw lmsLwc.html hosted with ❤ by GitHub
import { LightningElement, wire, track } from "lwc";
import {
publish,
MessageContext,
subscribe,
unsubscribe,
APPLICATION_SCOPE
} from "lightning/messageService";
import carSelected from "@salesforce/messageChannel/CarSelectionChannel__c";
export default class LmsLwc extends LightningElement {
subscription = null;
@track lastMessage = "";
@wire(MessageContext) mContext;
handleCarSelect(event) {
const payload = { source: "LWC", carName: event.target.value };
publish(this.mContext, carSelected, payload);
}
handleMessage(message) {
this.lastMessage = JSON.stringify(message);
}
//Method to subscribe to message channel. It will be invoked from connectedCallback
subscribeToCarSelectedChannel() {
if (!this.subscription) {
this.subscription = subscribe(
this.mContext,
carSelected,
(message) => this.handleMessage(message),
{ scope: APPLICATION_SCOPE }
);
}
}
//Method to unsubscribe from message channel. It will be invoked from disconnectedCallback
unsubscribeToCarSelectedChannel() {
unsubscribe(this.subscription);
this.subscription = undefined;
}
connectedCallback() {
this.subscribeToCarSelectedChannel();
}
disconnectedCallback() {
this.unsubscribeToCarSelectedChannel();
}
}
view raw lmsLwc.js hosted with ❤ by GitHub
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>49.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>

Create app builder page to see the result in one page

In order to confirm that the event is getting published and received in all three technologies, put above visualforce, aura and lwc components in a new App builder page and add the page to any app. It should looks like the screenshot at starting of this post.

All ways to communicate between lightning components (Aura and LWC)

There are a lot of different ways to communicate between different types of lightning components (lwc to lwc, lwc to aura, aura to lwc etc). In this blog we will cover all different ways of communicating between different types of lwc and aura components.

Communication patterns between Lightning components


Interactions with Visualforce Page

Still some features like PDF rendering are only supported in Visualforce. There is a chance you might need to interact between lightning components and visualforce components. Some examples are listed below,

  • Embedding visualforce page inside aura [COMING SOON]
  • Embedding aura app inside visualforce [COMING SOON]
  • Embedding visualforce page inside lwc [COMING SOON]
  • Embedding lwc inside visualforce page [COMING SOON]

Query all fields in an object using SOQL

Currently Salesforce does not allow you to query all fields in an object using wild card selectors. Even though it is usually not a best practice to query all fields in an object, sometimes it is useful to query all fields for debugging purposes

Simple way to query all fields in a object in single line is like below,

Database.query('SELECT ' + String.join(new List(Schema.getGlobalDescribe().get('Contact').getDescribe().fields.getMap().keySet()), ',') + ' FROM Contact');


This will be very handy while debugging. But please note that this is not the optimal way of doing this. If you are doing it in multiple places in code, it is recommended to cache the describe calls. An example of caching Schema.getGlobalDescribe() can be found in the blog How to get RecordType Id efficiently in Apex with caching

Script to deploy source/SFDX format code to your sandbox

Salesforce has two differnt formats to store code/metadata now. Conventional Metadata format and new Source/SFDX format.

Usually you can tell the format from folder structure and file format. If code is following some folder structure like force-app/main/default usually it will be in source format. But is it not guaranteed. A better way to confirm is to check object and field metadata files. If field metadata is included inside a .object file, that means that the code is in metadata format. In source/SFDX format, field metadata will be separated into separate files

Script to deploy source format code to sandboxes

Even if we use scratch orgs for development, we still need to use sandboxes for testing, UAT, training etc. We need to convert code in source format to metadata format before can properly deploy the whole codebase to these sandboxes or production. Use below shell script to make this metadata conversion and deployment easy.

#!/bin/bash
echo "Please enter alias of the environment that you want to deploy to"
read aliasInput
rm -rf mdapipackage
sfdx force:source:convert -d ./mdapipackage
sfdx force:mdapi:deploy -w 100 -d ./mdapipackage -u $aliasInput
cmd /k #this keeps the window open after execution so that we can read errors
view raw deploy.sh hosted with ❤ by GitHub

How to use

  • Create a shell file with .sh extension inside your project folder and copy paste above code.
  • Make the file executable with sudo chmod u+x pathToTheShellFileHere.
  • Authenticate your sandboxes/production using sfdx force:auth:web:login command
  • Now to deploy you can navigate to the project folder and run ./shellFileName.sh. It will ask for alias of the environment that you want to deploy to. Enter the alias and hit enter. Deployment starts.

Please note that this shell script works in Mac terminal and Linux shell only. If you are using windows, you can try to use bash shell there or write windows versions of the commands into a .bat file.

How to get RecordType Id efficiently in Apex with caching

It is very common needing to get recordtypeId when we are working in Salesforce. There are multiple ways to do it. Here we will go through different ways and find the best way.

Different approaches

  1. SOQL against RecordType standard object

    [SELECT Id, Name FROM RecordType WHERE Name = 'Master' AND SObjectType = 'Account']

    This approach count against the number of SOQL queries.

  2. Get recordtype ID in apex without caching

    public with sharing class RecordTypeUtil {
    //Do not use this
    public static Id getRecordTypeIdWithoutCache(String objectName, String recordtypeName) {
    Map<String, Schema.SObjectType> sObjectTypeMap = Schema.getGlobalDescribe();
    Schema.SObjectType sotype = sObjectTypeMap.get(objectName);
    Map<String, Schema.RecordTypeInfo> recordTypeMap = sotype.getDescribe().getRecordTypeInfosByName();
    return recordTypeMap.get(recordTypeName).getRecordTypeId();
    }
    }

    Since this approach do not cache operations like Schema.getGlobalDescribe() and sotype.getDescribe().getRecordTypeInfosByName() performance will not be great

  3. Get recordtype ID in apex with caching USE THIS APPROACH

    public with sharing class RecordTypeUtilWithCache {
    private static Map<String, Schema.SObjectType> sObjectTypeMap = Schema.getGlobalDescribe();
    private static Map<String, Map<String, Schema.RecordTypeInfo>> soTypeToRecordTypesMap = new Map<String, Map<String, Schema.RecordTypeInfo>>();
    public static Id getRecordTypeId(String objectName, String recordtypeName) {
    Schema.SObjectType sotype = sObjectTypeMap.get(objectName);
    Map<String, Schema.RecordTypeInfo> recordTypeMap = soTypeToRecordTypesMap.get(objectName);
    if(recordTypeMap == null) {
    recordTypeMap = sotype.getDescribe().getRecordTypeInfosByName();
    }
    Schema.RecordTypeInfo recordTypeInfo = recordTypeMap.get(recordtypeName);
    if (recordTypeInfo != null) {
    return recordTypeInfo.getRecordTypeId();
    }
    return null;
    }
    }

    Here we cache expensive schema describe operations. Allowing multiple calls to the same method to perform efficient and fast in same transaction.

    You can get recordtype Id using the syntax RecordTypeUtilWithCache.getRecordTypeId('Account', 'RecordTypeNameHere');

CTA Notes : Integration Architecture 1

When you prepare for Certified Technical Architect (CTA) certification, you need to have a lot of details readily available in your memory.

Here we will go through Integration Architecture related topics you need to keep in mind for CTA review board. We will skip some beginner level topics in this series to reduce the length.


Integration Patterns


1) Remote Process Invocation—Request and Reply

Salesforce calls remote system, waits for it to process the request, and makes updates based on response.
Patterns
  1. Enhanced External Services => Config way to invoke external REST services that gives OpenAPI 2.0 JSON schema format (Recommended Approach)
  2. UI element => User Action => Invoke Apex => Call out (Recommended Approach)
  3. Apex Trigger => asynchronous method => Call out
  4. Apex Batch => Call out
Considerations
  • Error Handling(Shown to user), Recovery (Changes are not saved in Salesforce till success response)
  • Idempotent Design Considerations => Guarantee that repeated invocations are safe. (deduplication, pass unique Id etc)
  • Security => One way SSL is enabled by default. Two way SSL (Self-signed/CA signed certs) can be enabled if needed, Can add one way hash/digital signature using crypto to ensure request authenticity, firewalls for remote system
  • Governor Limit => Max timeout is 120 secs, Can have only 10 requests that takes more than 5 secs to execute (Long running callout). To avoid set timeout to 5 sec / use continuation

2) Remote Process Invocation—Fire and Forget

Salesforce calls remote system, remote system acknowledges the request, Salesforce continues processes without waiting.
Patterns
  1. Platform events (Can be done through config/code)
  2. Outbound Messaging => Sending SOAP message from DML operations that fire workflow (Create/Edit), Salesforce retries if the system didn't respond
  3. Outbound Messaging with callback => Can send sessionId along with message. External system can use this session ID to access Salesforce REST/SOAP API to get more details about same record/other related records OR update result back to Salesforce.
  4. UI Component triggering Async callout => controller => callout => External system acknowledges and continues processing asynchronously
  5. Trigger => Async apex callout => external system
  6. Batch job => Apexbatch => Callout to external system
Considerations
  • Error Handling
    Callout => Remote system must handle errors that occur during asynchronous processing,
    Outbound message => Salesforce retries(15sec - 60min) up to 24 hours, external system must handle processing errors. Failures can be found in a queue for admins to monitor.
    Platform Events => Cannot rollback, replay ID can be used to get old events(upto 72 hours) after previous successful processing, Two options("Publish after Commit", "Publish Immediately")
  • Idempotent Design Considerations => Guarantee that repeated invocations are safe. (deduplication, pass unique Id etc)
  • Security Outbound Messaging=> One way SSL is enabled by default. Two way SSL can be enabled with "Outbound Messaging Certificate", IP whitelisting
  • Governor Limit => Max timeout is 120 secs, Can have only 10 requests that takes more than 5 secs to execute (Long running callout). To avoid set timeout to 5 sec / use continuation

3) Batch Data Synchronization

Two way data synchronization between Salesforce and external system.
Patterns
  1. Change Data Capture => Salesforce publishes change events for (CRUD, undelete) operations, near real time
  2. Replication using ETL tool => To send to Salesforce, we can use Bulk API, SOAP API, See if we can find ETL that supports change data capture against external system To extract from Salesforce, Use time/status to filter rows. There is a getUpdated() method in SOAP API.
  3. Remote Call-In & Remote Call-Out => Real time update, can cause a lot of taffice, special consideration on error handling, locking, can cause perofrmance issues
Considerations
  • ETL tools uses control tables to store last job time, and other values related to previous runs that can be used to filter the data that needs to be synced.
  • Use primary keys in both systems to match incoming data
  • Surrogate keys are anonymous integer primary keys without business meaning to do transformation
  • Get only updated data
  • Group child table loads(contact) by parent(accountId) to avoid locking/failure
  • Disable Triggers, workflow rules etc if not important

4) Remote Call-In

External system makes inbound request to make changes to Salesforce data. It is very common for companies to keep sales and service in Salesforce and order management in external system. In this case external system needs to update order status to Salesforce.
Patterns
  1. SOAP API => Can publish event (Syntax is like object creation) Enterprise WSDL & Partner WSDL can be used. Can create custom SOAP endpoint for complex transaction management
  2. REST API => Lightweight alternative for SOAP, No predefined contract, Composite resources option to make a series of updates in one API callback Composite resources => can use one request response for next input, couted as single API callback Bulk API is REST based
  3. Apex Web Services => For complex transaction support/custom logic, But more maintenance
  4. Apex REST services => Same as Apex Web Services but no need to consume service contract/WSDL
  5. Bulk API => Large volume CRUD.
  6. REST API to invoke an auto launched flow Salesforce
Considerations
  • Authentication => SSL V3, TLS are supported in SOAP API, Ciphers must have at least 128 bit key length.
    REST API cal use OAuth to get a token or use session ID obtained from Outbound message/SOAP API.
    Queries through SOAP API, can timeout after 120 sec.
    Volume Limits => SOAP login request must be less than 10KB, Only upto 3600 login() calls/user/hour Max 2GB for contentversion, 1MB max for event message size, Bulk API (Upto 10k batches/24hours, upto 10k records in a batch)
    For SOAP API, remote system (client) must participate in a contract-first implementation where the contract is supplied by Salesforce (for example, Enterprise or Partner WSDL).
    Custom SOAP webservice/Apex REST service can support cross-object transactions and custom logic, But always check if middleware is suitable to do these.
    An integration user can have up to 10 query cursors open at a time.
    Only 250k events per hour in case of high volumne platform events. Can replay up to 72 hours
    Salesforce APIs do not support reliable messaging protocols like WS-ReliableMessaging

5) UI Update Based on Data Changes

Create custom interface using streaming API to notify user of the changes in data.
Patterns
  1. Streaming API
  2. Platform Events
Considerations
  • Deliver and order of notifications is not guaranteed
  • Notifications are not generated for bulk API operations

6) Data Virtualization

Showing external data in Salesforce in real time without persisting it.
Patterns
  1. Salesforce Connect => maps data tables in external systems(SAP, MicroSoft etc) to external objects in Salesforce org
    External object features :- CRUD operations on external data, can define relations with custom objects, Can access in list views, tabs, page layouts, reports(limited) etc, Can view in Salesforce mobile app, available in global search and lookups, record feeds, apex, SOQL, SOSL, Salesforce APIs, change sets, packages etc, Can use Salesforce connect validator app to test connection.
    Can use one of the three connectors
    • OData 2.0/OData 4.0 connector to connect to OData producers
    • Cross-org adapter to connect to another Salesforce org using REST API
    • Custom adapter created via Apex => custom using Apex Connector Framework
  2. Request and Reply => Callout to external system using SOAP/REST protocol to call out
Considerations
  • Timeliness => User is waiting, max callout timeout is 120 sec, long running callout limits
  • Relationships => Lookup(Using Salesforce record Id as parent), External Lookup(Using external ID standard field for lookup), Indirect lookup (custom external and unique field for lookup)
  • Volume restrictions & rate limiting exist

Integration Pattern Categories

  • Data Integration - Syncing data. Often includes often include aspects of master data management (MDM), data governance, mastering, de-duplication, data flow design, and others.
  • Process Integration - Two or more apps integrated for completing business process.
    • orchestration (where one application is the central “controller”)
    • choreography (where applications are multi-participants and there is no central “controller”).
    complex to design & test, Needs to consider capabilities of systems, transactionality, Exception handling etc.
  • Virtual Integration - Interacting with data in another system without copying to Salesforce.
Pattern Selection Matrix
Integration Terms
  • Process choreography => mulitple systems working together without central authority
  • Service orchestration => Single central system coordinating activities of other systems
  • Transactionality => Support ACID (atomicity, consistency, isolation, durability). If complex multi-system transactions are required, go for middleware.
  • Long polling/Comet Programming => Client keeps a connection open to server. Bayeux is a protocol for transporting asynchronous messages. CometD is a scalable HTTP-based event routing bus that implements Bayeux protocol. Streaming API uses this. Websockets => Establishes two way communication channel, Server Side Events => server publishes and clients listens are alternatives.
  • Reverse Proxy Server => Receives requests on behalf of one or more servers, makes requests internally and returns results. Help to increase security, performance and reliability. Appache HTTP, nginx, IBM Webseal etc
  • more on encryption

Communication between independent lightning web component and Aura component - aurapubsub

You can use pubsub library or lightning message service to communicate between components that are not in same DOM hierarchy. These methods can be used to communicate between sibling components in an outer lwc component or between to lwc components that are dropped separately in lightning app builder.

Here we will go through an example of lwcComponentA that displays three car company names using radio button. When a car company is selected in the child component we will fire a carselected event from child component with the name selected by the user. Parent component will listen to this event and display the selected name in parent component. Parent component auraComponentB, will listen to this event and display the selected name in auraComponentB.

Example

1) Create a utility lwc component with name pubsub. Copy paste below code into it's JavaScript file. Please note that this is a utility component provided by Salesforce. But current version available in the link is doing pageRef check and it is not working as expected. So use below version shared by another Salesforce team member instead.

/**
* A basic pub-sub mechanism for sibling component communication
*
* TODO - adopt standard flexipage sibling communication mechanism when it's available.
*/
const events = {};
/**
* Registers a callback for an event
* @param {string} eventName - Name of the event to listen for.
* @param {function} callback - Function to invoke when said event is fired.
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
*/
const registerListener = (eventName, callback, thisArg) => {
if (!events[eventName]) {
events[eventName] = [];
}
const duplicate = events[eventName].find(listener => {
return listener.callback === callback && listener.thisArg === thisArg;
});
if (!duplicate) {
events[eventName].push({ callback, thisArg });
}
};
/**
* Unregisters a callback for an event
* @param {string} eventName - Name of the event to unregister from.
* @param {function} callback - Function to unregister.
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
*/
const unregisterListener = (eventName, callback, thisArg) => {
if (events[eventName]) {
events[eventName] = events[eventName].filter(
listener =>
listener.callback !== callback || listener.thisArg !== thisArg
);
}
};
/**
* Unregisters all event listeners bound to an object.
* @param {object} thisArg - All the callbacks bound to this object will be removed.
*/
const unregisterAllListeners = thisArg => {
Object.keys(events).forEach(eventName => {
events[eventName] = events[eventName].filter(
listener => listener.thisArg !== thisArg
);
});
};
/**
* Fires an event to listeners.
* @param {object} pageRef - Reference of the page that represents the event scope.
* @param {string} eventName - Name of the event to fire.
* @param {*} payload - Payload of the event to fire.
*/
const fireEvent = (pageRef, eventName, payload) => {
if (events[eventName]) {
const listeners = events[eventName];
listeners.forEach(listener => {
try {
listener.callback.call(listener.thisArg, payload);
} catch (error) {
// fail silently
}
});
}
};
export {
registerListener,
unregisterListener,
unregisterAllListeners,
fireEvent
};
view raw pubsub.js hosted with ❤ by GitHub

2) Create a utility lwc component with name auraPubSub Copy paste below code into it's JavaScript file.Please note that this is a utility component provided by Salesforce.

/**
* A Lightning Web component that wraps the methods of the pubsub ES6 module.
* This utility component allows Aura components to exchange messages (events) with Lightning web components.
* You can import an ES6 module directly into an Aura component, but in this case, we need to get the current page reference.
* You can't get the current page reference in an ES6 module; you must use a Lightning web component.
* Usage: add this component to an Aura component to publish events or subscribe to events using the pubsub utility.
* Do NOT use this component inside Lightning web components where you can import the pubsub module directly.
* The pubsub approach is used to provide a communication mechanism between sibling components assembled in a flexipage (App Builder) where traditional parent/child communication patterns are not available.
* Do NOT use this utility for parent/child communication.
*/
import { LightningElement, api, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import {
registerListener,
unregisterListener,
unregisterAllListeners,
fireEvent
} from 'c/pubsub';
export default class AuraPubsub extends LightningElement {
@wire(CurrentPageReference) pageRef;
connectedCallback() {
this.dispatchEvent(new CustomEvent('ready'));
}
@api
registerListener(eventName, callback) {
registerListener(eventName, callback, this);
}
@api
unregisterListener(eventName, callback) {
unregisterListener(eventName, callback, this);
}
@api
unregisterAllListeners() {
unregisterAllListeners(this);
}
@api
fireEvent(eventName, data) {
console.log('eventName :'+eventName+'data :'+data);
fireEvent(this.pageRef, eventName, data);
}
}
view raw auraPubsub.js hosted with ❤ by GitHub

3) Create a lwc component with name lwcComponentA

.green-border {
border: 3px solid green;
padding: 50px;
width: 250px;
height: 250px;
float:left;
}
<template>
<div class="green-border">
<h1>Component A (Publisher)</h1>
<br/>
<lightning-radio-group
name="cars" label="Cars" options={cars} value={selectedValue} type="radio" onchange={sendDataToComponentB}>
</lightning-radio-group>
</div>
</template>
import { LightningElement, wire } from 'lwc';
import { fireEvent } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';
export default class LwcComponentA extends LightningElement {
@wire(CurrentPageReference) pageRef;
cars = [
{ label: "Audi", value: "Audi" },
{ label: "Tesla", value: "Tesla" },
{ label: "BMW", value: "BMW" },
];
selectedValue = '';
sendDataToComponentB(event) {
fireEvent(this.pageRef ,'carselected', event.target.value);
}
}

4) Create a aura component with name auraComponentB

<aura:component>
<aura:attribute name='valueGotFromA' type='String'/>
<c:auraPubsub aura:id="pubsub" onready="{!c.handlePubsubReady}" />
<div class="red-border">
<h1>Component B (Subscriber)</h1>
<br/>
Value obtained from component A
<br/>
<b>{!v.valueGotFromA}</b>
</div>
</aura:component>
.THIS.red-border {
border: 3px solid red;
padding: 50px;
width: 250px;
height: 250px;
float:left;
margin-left: 10px;
}
({
handlePubsubReady: function (component) {
console.log('reached in aura component controller');
var pubsub = component.find('pubsub');
var callback = $A.getCallback(function (data) {
console.log('INSIDE');
console.log('called', JSON.parse(JSON.stringify(data)));
component.set('v.valueGotFromA', JSON.parse(JSON.stringify(data)));
console.log(component.get("v.valueGotFromA"));
// dataLayer.push(JSON.parse(JSON.stringify(data)));
// console.log('dataLayer', dataLayer);
});
console.log('going to register listner', callback);
pubsub.registerListener('carselected', callback);
console.log('after register listner');
},
})

5) Create a aura test app lwcToAuraSiblingTest in which lwcToAuraSiblingTest.App can contain both components

<aura:application extends="force:slds">
<div>
<c:lwcComponentA></c:lwcComponentA>
<c:auraComponentB></c:auraComponentB>
</div>
</aura:application>

6) Now if you open the aura app, it will look like

child to parent lwc communication

Explanation

In lwcComponentA component, when value of car is changed in the radio button, it calls JavaScript method in controller sendDataToComponentB. This function uses fireEvent method in pubsub library and fires a custom event with name carselected. Selected carname is also passed as data in the event payload.

auraComponentB component, has a listener added for carselected event. It is achieved by using registerListener method in auraPubsub utility. Inside handlePubsubReady methos we add a listener using the line registerListener('carselected', callback);. This basically tells "whenever a carselected event is received, go to callback inside handlePubsubReady method in JavaScript controller.

Finally when diplaySelectedCar method is called, we take value from the event payload and assigns to valueGotFromA variable in JavaScript

Things to Note

  • This is a common pattern to pass values between independent aura and lwc components in same page.
  • Make sure that you are using the pubsub and auraPubsub libraries shared above. Otherwise you might get an error pubsub listeners need a "@wire(CurrentPageReference) pageRef" property.

Communication between independent lightning web components - pubsub

You can use pubsub library or lightning message service to communicate between components that are not in same DOM hierarchy. These methods can be used to communicate between sibling components in an outer lwc component or between to lwc components that are dropped separately in lightning app builder.

Here we will go through an example of lwcComponentA that displays three car company names using radio button. When a car company is selected in this component we will fire a carselected event from lwcComponentA with the name selected by the user. A separate lwcComponentB, that is outside direct DOM hierarchy of lwcComponentA, will listen to this event and display the selected name in lwcComponentB.

Example

1) Create a utility lwc component with name pubsub. Copy paste below code into it's JavaScript file. Please note that this is a utility component provided by Salesforce. But current version available in the link is doing pageRef check and it is not working as expected. So use below version shared by another Salesforce team member instead.

/**
* A basic pub-sub mechanism for sibling component communication
*
* TODO - adopt standard flexipage sibling communication mechanism when it's available.
*/
const events = {};
/**
* Registers a callback for an event
* @param {string} eventName - Name of the event to listen for.
* @param {function} callback - Function to invoke when said event is fired.
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
*/
const registerListener = (eventName, callback, thisArg) => {
if (!events[eventName]) {
events[eventName] = [];
}
const duplicate = events[eventName].find(listener => {
return listener.callback === callback && listener.thisArg === thisArg;
});
if (!duplicate) {
events[eventName].push({ callback, thisArg });
}
};
/**
* Unregisters a callback for an event
* @param {string} eventName - Name of the event to unregister from.
* @param {function} callback - Function to unregister.
* @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
*/
const unregisterListener = (eventName, callback, thisArg) => {
if (events[eventName]) {
events[eventName] = events[eventName].filter(
listener =>
listener.callback !== callback || listener.thisArg !== thisArg
);
}
};
/**
* Unregisters all event listeners bound to an object.
* @param {object} thisArg - All the callbacks bound to this object will be removed.
*/
const unregisterAllListeners = thisArg => {
Object.keys(events).forEach(eventName => {
events[eventName] = events[eventName].filter(
listener => listener.thisArg !== thisArg
);
});
};
/**
* Fires an event to listeners.
* @param {object} pageRef - Reference of the page that represents the event scope.
* @param {string} eventName - Name of the event to fire.
* @param {*} payload - Payload of the event to fire.
*/
const fireEvent = (pageRef, eventName, payload) => {
if (events[eventName]) {
const listeners = events[eventName];
listeners.forEach(listener => {
try {
listener.callback.call(listener.thisArg, payload);
} catch (error) {
// fail silently
}
});
}
};
export {
registerListener,
unregisterListener,
unregisterAllListeners,
fireEvent
};
view raw pubsub.js hosted with ❤ by GitHub

2) Create a lwc component with name lwcComponentA

.green-border {
border: 3px solid green;
padding: 50px;
width: 250px;
height: 250px;
float:left;
}
<template>
<div class="green-border">
<h1>Component A (Publisher)</h1>
<br/>
<lightning-radio-group
name="cars" label="Cars" options={cars} value={selectedValue} type="radio" onchange={sendDataToComponentB}>
</lightning-radio-group>
</div>
</template>
import { LightningElement, wire } from 'lwc';
import { fireEvent } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';
export default class LwcComponentA extends LightningElement {
@wire(CurrentPageReference) pageRef;
cars = [
{ label: "Audi", value: "Audi" },
{ label: "Tesla", value: "Tesla" },
{ label: "BMW", value: "BMW" },
];
selectedValue = '';
sendDataToComponentB(event) {
fireEvent(this.pageRef ,'carselected', event.target.value);
}
}

3) Create a lwc component with name lwcComponentB

.red-border {
border: 3px solid red;
padding: 50px;
width: 250px;
height: 250px;
float:left;
margin-left: 10px;
}
<template>
<div class="red-border">
<h1>Component B (Subscriber)</h1>
<br/>
Value obtained from component A
<br/>
<b>{valueGotFromA}</b>
</div>
</template>
import { LightningElement } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub';
export default class LwcComponentB extends LightningElement {
valueGotFromA = '';
connectedCallback(){
registerListener('carselected', this.diplaySelectedCar, this);
}
disconnectedCallback() {
unregisterAllListeners(this);
}
diplaySelectedCar(carName) {
this.valueGotFromA = carName;
}
}

3) Create a parent lwc component with name parentLwc that can contain both components

<template>
<div class="parent">
<c-lwc-component-a></c-lwc-component-a>
<c-lwc-component-b></c-lwc-component-b>
</div>
</template>
view raw parentLwc.html hosted with ❤ by GitHub

3) Now if you surface the parent component in lightning experience, it will look like

child to parent lwc communication

Explanation

In lwcComponentA component, when value of car is changed in the radio button, it calls JavaScript method in controller sendDataTolwcComponentB. This function uses fireEvent method in pubsub library and fires a custom event with name carselected. Selected carname is also passed as data in the event payload.

lwcComponentB component, has a listener added for carselected event. It is achieved by using registerListener method in pubsub utility. Inside connected callback we add a listener using the line registerListener('carselected', this.diplaySelectedCar, this);. This basically tells "whenever a carselected event is received, call diplaySelectedCar method in JavaScript controller.

Finally when diplaySelectedCar method is called, we take value from the event payload and assigns to valueGotFromA variable in JavaScript

Things to Note

  • This is a common pattern to pass values between independent lwc components in same page.
  • Make sure that you are using the pubsub library shared above. Otherwise you might get an error pubsub listeners need a "@wire(CurrentPageReference) pageRef" property.

Communicating from child lightning web component to parent lightning web component

You need to fire CustomEvent from child component to communicate to a parent component.

Here we will go through an example of a child component that displays three car company names as a radio button. When a car company is selected in the child component we will fire a carchange event from child component with the name selected by the user. Parent component will listen to this event and display the selected name in parent component.

Example

1) Create an lwc component with name childLwc

.child-container {
border: 3px solid red;
margin:50px 10px;
padding: 0 20px;
}
view raw childLwc.css hosted with ❤ by GitHub
<template>
<div class="child-container">
<h1>Child Component</h1>
<lightning-radio-group
name="cars" label="Cars" options={cars} value={selectedValue} type="radio" onchange={sendDataToParent}>
</lightning-radio-group>
</div>
</template>
view raw childLwc.html hosted with ❤ by GitHub
import { LightningElement } from "lwc";
export default class ChildLwc extends LightningElement {
cars = [
{ label: "Audi", value: "Audi" },
{ label: "Tesla", value: "Tesla" },
{ label: "BMW", value: "BMW" },
];
selectedValue = '';
sendDataToParent(event) {
console.log(event.target.value);
this.dispatchEvent(new CustomEvent('carchange', { detail: event.target.value }));
}
}
view raw childLwc.js hosted with ❤ by GitHub

2) Create an lwc component with name parentLwc

.parent {
border: 3px solid green;
padding: 50px;
}
view raw parentLwc.css hosted with ❤ by GitHub
<template>
<h1>Parent Component</h1>
<div class="parent">
Value displayed in parent component => {valueSentByChild}
<c-child-lwc oncarchange={displayValueInParent}></c-child-lwc>
</div>
</template>
view raw parentLwc.html hosted with ❤ by GitHub
import { LightningElement } from 'lwc';
export default class ParentLwc extends LightningElement {
valueSentByChild = '';
displayValueInParent(event) {
this.valueSentByChild = event.detail;
}
}
view raw parentLwc.js hosted with ❤ by GitHub

3) Now if you surface the parent component in lightning experience, it will look like

child to parent lwc communication

Explanation

In childLwc component, when value of car is changed in the radio button, it calls JavaScript method in controller sendDataToParent. This function then dispatches/fires a custom event with name carchange. For every custom event, if you are passing data, you need to pass it as a JSON object with detail attribute.

parentLwc component, has a listener added for carchange event. It is achieved by adding on+eventname attribute while refering child component in html. In our case it is oncarchange attribute added at line 5 of parentLwc component html. This basically tells "whenever a carchage event is received, call displayValueInParent method in JavaScript controller.

Finally when displayValueInParent method is called, we take value from the event.details and assigns to valueSentByChild variable in JavaScript

Things to Note

  • This is a very common pattern to pass values from child component to parent component
  • Make sure that you are using detail attribute while sending data
  • Make sure that your event name is all small letters. ie carchange instead of carChange
  • Make sure that your listener name is all small letters. ie oncarchange instead of oncarChange

Communicating from parent lightning web component to child lightning web component

It is easy to communicate/send data from a parent lightning web component to a child lwc component. You can define a variable in the child lwc component javascript file, annotated it with @api annotation and finally pass value from parent component to the child component by using this variable name.

Example

1) Create an lwc component with name childLWCComponent

<template>
Value received from parent lwc component to child lwc component ==> <b>{childVariable}</b>
</template>
import { LightningElement, api } from 'lwc';
export default class ChildLWCComponent extends LightningElement {
@api childVariable;
}

2) Create an lwc component with name parentLWCComponent

<template>
<!-- Notice that the uppercase characters in child component name needs to be replaced with - character -->
<c-child-l-w-c-component child-variable="Value sent from Parent"></c-child-l-w-c-component>
<c-child-l-w-c-component child-variable={dynamicVariable}></c-child-l-w-c-component>
</template>
import { LightningElement } from 'lwc';
export default class ParentLWCComponent extends LightningElement {
dynamicVariable = 'Dynamic value from Parent';
}

If you surface the parent component in lightning experience, it will looks like

parent to child lwc communication

Explanation

Here childLWCComponent has a javascript variable childVariable annotated with @api annotation. Parent component can pass value using this attribute name. But please note that camel case variable names in child component is converted to kebab case in parent component.

Use Cases

This is the most common form of communication/exchange of data between lwc components. Usually complex lightning apps are built as a combination of multiple child lwc components. ie a parent lwc component contining multiple child lwc components. This parent component will retain data that is needed by all child components and sends it to child components using variables defined in child component using @api annotation.


How to display a modal popup using aura components

When you build custom interfaces it is very common need to show a modal window or a popup window. In this example we will go through how we can build a reusable modal window using lightning aura components. If you are looking for a lightning web component (lwc) version of modal/popup component please check out lwc modal.

Use cases

  • Display modal window or popup window in user interface
  • Show Yes/No confirmation modal popups

Steps to Implement

STEP 1 :- Create a component event with name Util_PressEvent. This event is used to track click event on buttons that can be used to add listeners on modal component. Find the event file and metadata below,

<aura:event type="COMPONENT" description="Generic component event for all press actions" />
<?xml version="1.0" encoding="UTF-8"?>
<AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<description>This event is used to represent click/press events.</description>
</AuraDefinitionBundle>

STEP 2 :- Create an aura component with name Util_ModalWindow. This component will contain all code needed to show and hide the modal popup,

<aura:component >
<aura:attribute name="title" type="string" description="Title of the popup" />
<aura:attribute name="cancelLabel" type="string" default="" description="Label for cancel button" />
<aura:attribute name="confirmLabel" type="string" default="Okay" description="Label for OK button" />
<aura:attribute name="isHidden" type="Boolean" default="true" description="Flag used to toggle popup" />
<aura:registerEvent name="onSaveClick" type="c:Util_PressEvent"/>
<aura:registerEvent name="onCancelClick" type="c:Util_PressEvent"/>
<aura:method name="changeVisibility" action="{!c.changeVisibility}" description="show/hide this component">
<aura:attribute name="isShow" type="Boolean" default="false" />
</aura:method>
<div>
<div aria-hidden="{!v.isHidden}" role="dialog"
class="{!v.isHidden ? 'slds-modal slds-modal__close slds-fade-in-close' : 'slds-modal slds-modal__open slds-fade-in-open'}">
<div class="slds-modal__container">
<div class="slds-modal__header">
<h2 class="slds-text-heading--medium">
{!v.title}
</h2>
<button class="slds-button slds-modal__close slds-button--icon-inverse" title="Close" onclick="{!c.defaultCloseAction}">
<!-- <c:svg svgPath="/resource/SLDS214/assets/icons/utility-sprite/svg/symbols.svg#close" category="standard" size="large" name="close" /> -->
X
</button>
</div>
<div class="slds-modal__content slds-p-around--medium">
<div>
{!v.body}
</div>
</div>
<div class="slds-modal__footer">
<aura:if isTrue="{! !empty(v.cancelLabel)}">
<button class="slds-button slds-button--neutral" onclick="{!c.fireCancelEvent}">{!v.cancelLabel}</button>
</aura:if>
<button class="slds-button slds-button--brand" onclick="{!c.fireSaveEvent}">{!v.confirmLabel}</button>
</div>
</div>
</div>
<div class="{!v.isHidden ? 'slds-backdrop slds-backdrop--close' : 'slds-backdrop slds-backdrop--open'}" aura:id="backdrop" ></div>
</div>
</aura:component>
.THIS {
}
@media screen and (max-width: 900px) and (min-width: 300px){
.THIS.cLIModalWindow .slds-modal__footer .slds-button {
min-width: 0;
margin: .20em;
padding: .20em;
}
.THIS.cLIModalWindow .slds-modal__footer {
padding: .75rem 0;
text-align: right;
}
}
({
defaultCloseAction : function(component, event, helper) {
component.set("v.isHidden", true);
},
fireSaveEvent : function(component, event, helper) {
var event = component.getEvent("onSaveClick");
event.fire();
},
fireCancelEvent : function(component, event, helper) {
var event = component.getEvent("onCancelClick");
event.fire();
},
changeVisibility: function(component, event, helper) {
var isShow = event.getParam('arguments').isShow;
console.log("isShow ==>",isShow);
component.set("v.isHidden", !isShow);
}
})

STEP 3 :- You are all set to use the modal component. Let us use the modal component inside an aura component to show a modal popup. Here we are using a parent aura component with name DemoAuraComponent to show modal popup.

<aura:component implements="force:appHostable">
<aura:attribute name="isModalHidden" type="Boolean" default="true" description="Flag to control modal window" />
<lightning:card title="Modal Demo">
<p class="slds-p-horizontal_small">
This is a parent component from where we need to show the modal window
</p>
<lightning:button variant="brand" label="Show Modal" title="Show Modal" onclick="{! c.openModal }" />
</lightning:card>
<c:Util_ModalWindow isHidden="{!v.isModalHidden}" cancelLabel="cancel" onSaveClick="{! c.onConfirm }" onCancelClick="{! c.onCancel }">
This is the modal body. Other Aura/LWC components can be embedded here.
</c:Util_ModalWindow>
</aura:component>
({
openModal : function(component, event, helper) {
component.set("v.isModalHidden", false);
},
onConfirm: function(component, event, helper) {
console.log('Will be called when confirm button is clicked on modal');
component.set("v.isModalHidden", true);
},
onCancel: function(component, event, helper) {
console.log('Will be called when cancel button is clicked on modal');
component.set("v.isModalHidden", true);
}
})

Explanation

Here we can add any content including other aura components between opening and closing tags of Util_ModalWindow. All the content between the tags will be rendered as the body of the modal window.

Example

  • Before opening Modal

  • After opening modal


How to send notifications to bell icon in Salesforce

Salesforce allows us to send custom notifications to bell icon in lightning experience using "custom notification" feature. These custom notifications can be sent using process builders or flows or REST API.

Unfortunately there is no native method in apex that you can just call to send notification yet. One work around is to make a a callout to Salesforce rest endpoint from Apex. In this blog we will go through examples of sending custom notifications through different channels.

Use cases

  • Can be used to send notifications to desktop/lightning experience bell icon or to Salesforce mobile app as push notification
  • Any scenario of when you need to get user's attention in Salesforce. For example, notify sales agent about an activity on an opportunity they are working on, notify administrator about failures in a critical API call etc

Using apex to send notification


public class NotificationManager {
public static Id notificationId = [Select Id, CustomNotifTypeName from CustomNotificationType WHERE CustomNotifTypeName = 'Error Notification' LIMIT 1].Id;
//Method to send notifications
public static void sendNotification(List<Id> receipients, String title, String body, Id targetId) {
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(Url.getOrgDomainUrl().toExternalForm() + '/services/data/v48.0/actions/standard/customNotificationAction');
req.setMethod('POST');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
req.setHeader('Content-Type', 'application/json');
//Constructing payload
Map<String, Object> notificationObjMap = new Map<String, Object>();
notificationObjMap.put('title', title);
notificationObjMap.put('body', body);
notificationObjMap.put('customNotifTypeId', notificationId);
notificationObjMap.put('targetId', targetId);
notificationObjMap.put('recipientIds', receipients);
Map<String, Object> payload = new Map<String, Object>{'inputs' => new List<Object>{notificationObjMap}};
req.setBody(JSON.serialize(payload));
HttpResponse res = http.send(req);
System.debug(res.getBody());
}
}

Explanation

Salesforce still hasn't made the ability to send custom notifications from apex available in apex. So we need to call Salesforce REST API (Custom Notification Actions) from apex to to trigger the notifications.

In this example we are expecting a custom notification to be created from setup with name Error Notification. Once you have a the custom notification setup, you can just call the method using below syntax.


List<Id> recipientList = new List<Id>{Userinfo.getUserId()}
String title = 'Please check the opportunity';
String body = 'Important change in opportuntiy';
Opporutnity opp = [SELECT Id FROM Opportunity LIMIT 1];
NotificationManager.sendNotification(recipientList, title, body, opp.Id);

Steps to send notification through configuration

  1. Create a custom notification from setup


  2. Create a process builder entry to fire on updates of opportunity records


  3. Example. Update any opportunity and see result in bell icon


How to make a REST callout/integration from Salesforce to Github REST API

Usually to integrate with an external system that supports REST API, you need to make REST callouts from Salesforce. This blog demonstrates how to make a REST callout from Salesforce to Github.

Demo



Code Sample


//Integration with Github API
public class GithubApi {
public static String getGithubUserDetails(String githubUserHandle) {
Http http = new Http();
//Create a HttpRequest instance
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.github.com/users/'+githubUserHandle);
request.setMethod('GET');
// Make the API call and get response
HttpResponse response = http.send(request);
String responseString = response.getBody();
//Usually you get response from REST APIs in String format
//You can process these resonse
return responseString;
}
}
view raw GithubApi.cls hosted with ❤ by GitHub

Steps

  • Add remote site settings
  • Create GithubApi class with a method to make API call
  • Copy code from code snippets and save class
  • Execute the class in developer console using System.debug(GithubApi.getGithubUserDetails('SalesforceCodes'));

How to customize Salesforce lightning community layout - Custom Theme layout

Salesforce lighting community comes with a number of predefined layouts. But most of the times these layouts do not match your requirements/company branding. In this blog we will go through how we can customize the layout of a Salesforce lightning community. It is done by creating "custom theme layout" using aura components.

Use cases

  • Provide better user experience and branding through Salesforce lightning community
  • Build a custom, mobile responsive layout that is not available in standard predefined templates like Napili template.

<aura:component implements="forceCommunity:themeLayout">
<aura:attribute name="header" type="Aura.Component[]"/>
<aura:attribute name="footer" type="Aura.Component[]"/>
<div class="custom-wrapper">
<div class="slds-grid slds-grid_vertical-align-center" style="border: 1px solid red;">
<div class="slds-col">
<span>Company Logo Here</span>
</div>
<div class="slds-col">
{!v.header}
</div>
<div class="slds-col">
<span>Header right content</span>
</div>
</div>
<div class="content">
{!v.body}
</div>
<div class="slds-grid slds-grid_vertical-align-end" style="border: 1px solid red;">
<div class="slds-col">
<span>Footer left</span>
</div>
<div class="slds-col">
{!v.footer}
</div>
<div class="slds-col">
<span>Footer right</span>
</div>
</div>
</div>
</aura:component>

Explanation

Here we are creating a custom theme layout that we can use to customize the layout of community pages. First step is to create an aura component that implements forceCommunity:themeLayout interface.
Now as part of the layout, you might have some things that are static between different pages (For example company logo on top left). Also you might have some content that changes between different pages in the community that uses the layout. All static content that doesn't change between pages like logo can be directly coded into the layout itself. Then you need to create attributes that are array of aura components to make some sections dynamic. In the above example we are defining a header attribute at line 2 as an array of Aura.Component. Then we use that attribute on line 10 using the syntax {!v.header}. This simply says that When the layout is renderd in community builder, provide a drag and dropable area of lightning components at line 10.

How to use

  • Create an aura component with name MyCustomLayout
  • Copy paste above code into the component.
  • Go to the design file of the aura component (MyCustomLayout.design) and give a title you like in the opening tag like design:component label="My Responsive Theme Layout"
  • Now go to your lightning community builder theme settings and define a theme layout, Setup => All communities => Builder => Gear icon in left floating menu => Theme => Configure Defining Theme Layout
  • Once you have defined a custom theme like this, you will be able to use that in any existing community builder pages or new community builder pages that you create
  • To use the new layout in an existing community page, simple select settings of the page and check "Override the default theme layout for this page" option at the bottom.
    Changing theme layout of existing page

How to create a scratch org with "Non Profit Starter Pack (NPSP)"

Salesforce scratch orgs makes it very easy to track metadata changes. But if you are developing an app that involves non profit starter pack, it is usually tricky to setup a scratch org with NPSP packages. In this blog, we will go through different steps needed to setup a scratch org with NPSP packages.
NPSP is basically a combination of multiple appexchange packages. But to install these your sandbox/scratch org needs to meet certain conditions. These prerequisites are adding certain recordtypes to Account and Opprotunity and adding an Opprotunity sales process. Metdata needed for these can be found in the link - https://github.com/SalesforceFoundation/NPSP/tree/master/unpackaged/pre. If you wish to run below bash script directly, you need to copy individual metadata files from above NPSP github repo into a single npsp-dependencies folder in deployable format with a combinted package.xml file.

Use cases

  • Easy development of enhancements to NPSP in scratch orgs
  • Other approaches like using Cumulus CI are usually more time consuming and difficult to setup

#!/bin/bash
# Make sure that you have set an org as default dev hub. Usually your production environment will be your devhub environment.
#sfdx force:config:set defaultdevhubusername=productionUsernameHere
echo "Creating scratch org"
sfdx force:org:create -s -f config/project-scratch-def.json -a myScratchOrg -d 30
sfdx force:org:open
# Before running below line, copy content from inner folders in the github location -
# https://github.com/SalesforceFoundation/NPSP/tree/master/unpackaged/pre
# and create a single folder "npsp-dependencies" with account, opportunity object files and a combined package.xml
echo "Deploying pre dependency for NPSP packages"
sfdx force:mdapi:deploy -w 100 -d ./npsp-dependencies -u myScratchOrg
echo "(1/6) NPSP Installing Contacts & Organizations 3.7.05"
echo "y" | sfdx force:package:install -p 04t80000000gYcfAAE -w 15 -u myScratchOrg
echo "(2/6) NPSP Installing Household 3.9.0.8"
echo "y" | sfdx force:package:install -p 04t80000000jYrOAAU -w 15 -u myScratchOrg
echo "(3/6) NPSP Installing Affiliations 3.6.0.5"
echo "y" | sfdx force:package:install -p 04t80000001AVBMAA4 -w 15 -u myScratchOrg
echo "(4/6) NPSP Installing Relationships 3.6.0.5"
echo "y" | sfdx force:package:install -p 04t80000000tpCGAAY -w 15 -u myScratchOrg
echo "(5/6) NPSP Installing Recurring Donations 3.10.0.4"
echo "y" | sfdx force:package:install -p 04t80000000tpCBAAY -w 15 -u myScratchOrg
echo "(6/6) NPSP Installing Nonprofit Success Pack 3.116.0.5"
echo "y" | sfdx force:package:install -p 04t1Y000001I8yUQAS -w 15 -u myScratchOrg
echo "if any installation failed, retry it by running corresponding command."
echo "pushing package code"
sfdx force:source:push -f
view raw npspsetup.sh hosted with ❤ by GitHub

Explanation

Above shell script will work for Mac and Linux users only. If you are a windows user, you might need to copy-paste and run individual commands separately from windows command line. As part of this script we are first creating a scratch org. Then we open the scratch org in browser. After that at line 14 we install all the prerequisites for NPSP package. Then we install 6 NPSP appexchange packages one after another. Please note that in future Salesforce might upgrade these packages and the package IDs will change. In that case find package ID corresponding to the upgraded version and change in the script.

How to use

Make sure that you are inside an sfdx project folder. If not, create a SFDX project using vscode or SFDX cli sfdx force:project:create -n projectName. As first step, make sure that you have copied all dependencies from https://github.com/SalesforceFoundation/NPSP/tree/master/unpackaged/pre to a folder with name npsp-dependencies inside your repository. Make sure you have set a devhub environment as default that you can use for spinning off scratch orgs( sfdx force:config:set defaultdevhubusername=productionUsernameHere ). Copy above code into a shell script file. *Example createScratchOrg.sh) Make sure that your shell script file has proper permissions by running chmod u+x createScratchOrg.sh. Finally run the shell script from terminal using ./createScratchOrg.sh.
Since it is installing 6 different appexchange packages as part of NPSP, the whole process might take some time. You can see the progress.

How to connect to Salesforce metadata API from a Visualforce Page

Meatadat API allows you to connect to Salesforce to create, modify and delete Salesforce metadata. This API is designed for external systems/clients to connect to Salesforce. But there will be use cases where you need to connect to metadata API from Salesforce itself and make changes to the metadata in same org/sandbox.

In this blog we will go through creating a custom visualforce page that you can use to connect to Salesforce metadata API and create objects in Salesforce.

Use cases

  • Update metadata(create objects, create fields, page layouts etc) from visualforce
  • To provide a single user interface for admins to do different actions that otherwise would require going through different setup sections (Example :- post installation setup of complex appexchange apps)

<apex:page>
<apex:includeScript value="https://cdnjs.cloudflare.com/ajax/libs/jsforce/1.9.1/jsforce.min.js" />
<script>
//Initializing jsforce library with session token
var conn = new jsforce.Connection({ accessToken: '{!$Api.Session_ID}' });
//Method to create object
function createObject() {
let objLabel = document.querySelector('#objLabel').value;
let objApi = document.querySelector('#objApi').value;
if(!objApi || !objLabel) {
alert('Please enter a label and api name');
return;
}
var metadata = [{
fullName: objApi,
label: objLabel,
pluralLabel: objLabel,
nameField: {
type: 'Text',
label: 'Name'
},
deploymentStatus: 'Deployed',
sharingModel: 'ReadWrite'
}];
conn.metadata.create('CustomObject', metadata, function(err, result) {
if (err) { alert(JSON.stringify(err)); }
if(result) {
alert(JSON.stringify(result));
}
});
}
</script>
<div style="margin:100px;">
<b>Object Label</b> &nbsp;
<input type="text" id="objLabel" placeholder="Please enter a label"/><br/>
<b>Object API Name</b>
<input type="text" id="objApi" placeholder="Value with __c" /><br/>
<input type="button" onclick="createObject();" value="Create Object"></input>
</div>
</apex:page>
view raw metadata.html hosted with ❤ by GitHub

Explanation

We are using jsforce library to interact with Salesforce metadata. In this particular example we are creating a custom object in Salesforce. First we load jsforce library from CDN using apex:includeScript tag. Then we initialize salesforce connection in jsforce library with logged in user's session id. createObject function reads label and API name of desired object from HTML input tags and calls jsforce method available on connection instance conn.metadata.create. We are passing metadata type, object metadata as a JSON array and a callback function to this method. Once Salesforce has processed the request, callback will be called with error/success response. To simplify things we are just alerting the result we are getting in the callback. But we can definitely build a good user interface and provide complex metadata manipulation functionalities using the ability to interact with metadata.

Creating an object is just an example usecase. You can do a lot more things with jsforce library. Examples can be found in jsforce documenation. jsforce metadata


How to use

  • Create a visualforce page in your org
  • Copy paste above code into your page. There is no controller or other dependencies.
  • Go to below url to see the resulting page
    {{yourSalesforceBaseUrl}}/apex/{{NameOfThePageYouCreated}}

Reference - Git commands

Version control systems allow you to track all changes made to your codebase and enables you to do things like reverting to an old version of code, developing features separately without affecting other people's changes etc. There are many version control tools like Git, SVN/subversion etc. Git is a very popular version control system out there. It is a distributed version control system, ie - every user will have their own seprate copy of code and can continue development even if there is no connectivity to server. From Salesforce development perspective, using version control systems like git became more important than earlier with Salesforce DX(SFDX).

Basics

This article assumes that you know basics of git. Once you are familiar with basics, you can use this reference article to quickly find common git commands. If you want to go through basics of git please checkout this git guide.


#GIT Basic Commands
#Creating new repo
git init [project-name]
#Cloning existing repo
git clone [url]
#List all new/modified files that needs to be committed
git status
#Shows difference in files that is not yet staged
git diff
#Add particular file for staging
git add [file]
#Add all files for staging
git add .
OR
git add -A
#Show difference between staging and latest local file
git diff --staged
#Show difference with a commit
git diff HEAD
git diff HEAD^
git diff HEAD~6
#Show difference between commits
git diff HEAD^..HEAD
git diff sha1..sha2
git diff master featureBranch
git diff --since=2.month.ago --until=1.week.ago
#Show all changes to a single file by lines with date
git blame index.html --date short
#Unstage file with preserved content
git reset [file]
#Committing file
git commit -m "Message"
#Adding and committing file in one shot
git commit -am "Message"
#List all branches
git branch
git branch -r #shows remote branches
git remote show origin #Shows more details about each local and remote branch
git remote prune origin #Cleans up deleted remote branches
#Create new branch
git branch [branch-name]
#Checking out/Switching to specific branch
git checkout [branch-name]
#Creating and Checking out a branch in one step
git checkout -b [branch-name]
#Creating a branch and checking out from a different branch
git checkout -b myfeature develop
#Merging specific branch's commits into the current branch
git merge [branch]
#Adding remote link
git remote add origin ssh://git@stash.ddhive.com:7999/test/test.git
git remote rm origin
git remote rename origin newname
#List all remotes
git remote -v
#Publishing branch to remote repo
git push -u origin [branch-name]
#Pushing tags to remote
git push <remote> --tags
#Deleting branch locally
git branch -d [branch-name] --> If changes are fully merged
git branch -D [branch-name] --> Force deleting even if changes are not fully merged
#Deleting branch from remote
git push origin --delete [branch-name]
OR
#git push origin :[branch-name]
#Delete file and stage deletion
git rm [filename]
#Remove from version control and preserve locally
git rm --cached [filename]
#Move/rename file and prepare for commit
git mv [oldname] [newname]
#Temporarily stash all modified tracked files
git stash
#Restores latest stashed files
git stash pop
#OR
git stash apply + git stash drop
#Lists all stashed changesets
git stash List
#Delete most recent stashed changeset
git stash drop
#Putting to stash and applying back
git stash save
or
git stash save "message to stash"
or
git stash save --keep-index #To stash and keep staged files as such
or
git stash save --include-untracked #To stash and keep untracked files also
git stash apply
or
git stash apply stashName
git stash branch branchName stashName # to create branch from stash
git stash show stashName # to get details of stash
git stash clear # clear all stashes
#Enabling color
git config --global color.ui true
#Listing all config
git config --list
#List version history
git log
#One line log
git log --pretty=oneline
#Log with change details
git log --oneline -p
#Log with how many lines inserted/remoted
git log --oneline --stat
#Log with graphical branching
git log --oneline --grph
#Filtering logs with time range
git log --since=2.month.ago --until=1.week.ago
#List version history in graphical manner
git log --graph
#Creating alias for log
git config --global alias.mylog "log --pretty=format:'%h %s [%an]' --graph"
git mylog
#Alias for status
git config --global alias.st status
git st
#List version history of a particular file
git log --follow [filename]
git diff [first-branch]...[second-branch]
#Show changes on a particular commit
git show [commit]
#Reset head to a particular commit, preserving changes locally
git reset [commit]
#Resets head and discards all history
git reset --hard [commit]
#Reset last two commits
git reset --hard HEAD^^
#Undo last commit and put the changes back to staging
git reset --soft HEAD^
#Amending last commit
git commit -amend -m "New message"
#Downloads all history of a particular branch
git fetch [branch-name]
#Download history and incorporate to local branch
git pull
git pull --rebase #does fetch + rebase. It is better.
git push
#Resetting head to an old point
git reset --hard 6ae0aa9c5cb678
#Clearing untracked files after hard reset
git clean -f -d
#Ignoring changes to two files
git checkout -- file1.txt file2.html
#Merging feature branch to develop branch
$ git checkout develop
#Switched to branch 'develop'
$ git merge --no-ff myfeature
#Updating ea1b82a..05e9557
#(Summary of changes)
$ git branch -d myfeature
#Deleted branch myfeature (was 05e9557).
$ git push origin develop
#Creating release branch from develop
$ git checkout -b release-1.2 develop
#Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
#Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
#[release-1.2 74d9424] Bumped version number to 1.2
#1 files changed, 1 insertions(+), 1 deletions(-)
#Merging changes to master and tagging
$ git checkout master
#Switched to branch 'master'
$ git merge --no-ff release-1.2
#Merge made by recursive.
#(Summary of changes)
$ git tag -a 1.2
#Merging same changes to develop
$ git checkout develop
#Switched to branch 'develop'
$ git merge --no-ff release-1.2
#Merge made by recursive.
#(Summary of changes)
#Deleting release branch
$ git branch -d release-1.2
#Creating hotfix branch and merging to master and develop branches
$ git checkout -b hotfix-1.2.1 master
#$ ./bump-version.sh 1.2.1
$ git commit -a -m "Bumped version number to 1.2.1 and applied fixes"
$ git checkout master
$ git merge --no-ff hotfix-1.2.1
$ git tag -a 1.2.1
$ git checkout develop
$ git merge --no-ff hotfix-1.2.1
#List Tags
git tag
#Checkout particular tag code
git checkout v0.1
#Creating new tag
git tag -a v0.2 -m "v0.2"
#Pushing tags to remote
git push --tags
#Rebase applies local commits after remote commits. This avoids a merge commit
#Rebasing master with remote master
git fetch
git rebase origin/master
#If conflict is there fix it, git add, then
git rebase --continue
#Manipulating last 3 commits using rebase
git rebase -i HEAD~3
#Then below options
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#Clone entire repo & rewriting history
git clone repoName filterRepo
git filter-branch --tree-filter anyShellCOmmand #[takes each commit, runs command, recommits]
git filter-branch --tree-filter anyShellCOmmand -- --all # to run in all commits and all branches
git filter-branch --tree-filter 'rm -f passwords.txt'
git filter-branch --tree-filter 'find . -name "*.mp4" -exec rm {} \;'
#or run below command to operate in staging are with git command
git filter-branch --index-filter 'git rm --cached --ignore-unmatch passwords.txt' --prune-empty -- --all
#To remove empty commits
git filter-branch -f --prune-empty -- --all
#Line endings
Unix /n
git config --global core.autocrlf input
Windows /n/r
git config --global core.autocrlf true
#Sample .gitAttributes
* text=auto
*.rb text
*.js text
*.bat text eol=crlf
*.sh text eol=lf
*.png binary
git cherry-pick commitId
git cherry-pick --edit commitId # to edit commit message
git cherry-pick --no-commit commit1 commit2 # pick multiple and stage
git cherry-pick -x commitId # keep same SHA
git cherry-pick --signoff commitId # adds name of cherry-picker
#Submodules
git submodule add remoteRepoName
#Any new local commits to submodule should be pushed to submodule remote and main repo remote seprately
#Any new user need to run below after clone
git submodule init
git submodule update # should be be used to refresh the repo anytime
#submodule update brings repo to headless state. To fix run below,
git checkout master
git merge hashShownInHeadlessState
#To abort push if submodule is not pushed
git push --recurse-submodules=check
#To automatically push submodules
git push --recurse-submodules=on-demand
#Alias it as below
git config --global alias.pushall "push --recurse-submodules=on-demand"
#Git stores backup in reflogs (Only available locally)
git reflog #-> to see backup. even after hard reset
or
git log --walk-reflogs #-> gives more details
view raw Git Commands.sh hosted with ❤ by GitHub

Explanation

You need to have a very high level idea about git to use this reference article. Just search in the page to find commands for common operations like cloning repo, creating branch, committing code, reverting code, stashing code etc.


How to use

Install Git CLI corresponding to your operating system from git website. Start running commands from above command reference.

Please note that if you do not want to use command line git, you can always depend on git clients like Github Desktop, SourceTree, TortoseGit, GitKraken and more others


How to use custom iterator in apex batch class

Apex batch classes are used to process large volume of records that can hit governor limits if processed synchronously. Batch classes are used to split these large set of records into small chunks and execute as separate transactions. Batch class can work on up to 50M sObject records using QueryLocator or upto 50k records using Iterator. Using list of sObjects is a common pattern and you can find a sample batch class template here. There are two ways of using iterator approach. it can either be a or a There are cases where using a Iterator is more meaningful. You can find some use cases below,

  1. Use a custom iterator defined using CustomIterable interface
  2. You can just use standard apex collections like list as iterator. Because they are implementing Iterable interface behind the scenes

Use cases

  • If your batch class needs to operate on data obtained from two unrelated objects, you can create a custom iterable class and return combined data from both objects as a list of iterable from start method
  • Say you have an API that returns ~5k product Ids and then you need to hit another API for each of these products to get more details about each product. You can return a list of product Ids from start method to achieve this.

/*******************************************************************************************
* @Name IteratorExampleBatch
* @Author FirstName LastName <author@email.com>
* @Date 01/13/2020
* @Group Apex Batches
* @Description This batch class demonstrates use of iterator in apex batch class.
*******************************************************************************************/
/* MODIFICATION LOG
* Version Developer Date Description
*-------------------------------------------------------------------------------------------
* 1.0 Firstname 01/13/2020 Initial Creation
*******************************************************************************************/
global class IteratorExampleBatch implements Database.Batchable<String>, Database.AllowsCallouts {
//Make sure you add below endpoint to remote site settings
private final String API_BASE = 'https://th-apex-http-callout.herokuapp.com/';
/**************************************************************************************
* @Description This method gets all animals, get corresponding animal IDs and sends to
* execute for processing
* @Param bc - BatchableContext
* @Return Iterable<String> - List of animals whose details need to be retrieved
**************************************************************************************/
global Iterable<String> start(Database.BatchableContext bc) {
List<String> animalIdList = new List<String>();
//Move the API call code to a separate class for reusability
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(API_BASE+'animals');
request.setMethod('GET');
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
Map<String, Object> resultMap = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
for (Object animal: (List<Object>) resultMap.get('animals')) {
animalIdList.add((String)animal);
}
}
return animalIdList;
}
/**************************************************************************************
* @Description This method gets details of individual animal by hitting API
* @Param bc - BatchableContext
* @Param accountanimalIdListList - Chunk of animal Ids that needs to be processed
* @Return NA
**************************************************************************************/
global void execute(Database.BatchableContext bc, List<String> animalIdList){
//Do some heavy processing here. In this case get more details about each animal :)
//Move the API call code to a separate class for reusability
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('GET');
for(String animalId: animalIdList) {
System.debug('Getting details about'+animalId);
request.setEndpoint(API_BASE+'animals/'+animalId);
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
Map<String, Object> resultMap = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
System.debug('Got details about animal ' + response.getBody());
//Bulkify the results and store in records
}else {
system.debug('Unable to get about animal => '+response.getStatusCode()+response.getBody());
}
}
}
/**************************************************************************************
* @Description This method executes any post-processing operations
* @Param bc - BatchableContext
* @Return NA
**************************************************************************************/
global void finish(Database.BatchableContext bc){
// execute any post-processing operations
}
}

Explanation

Apex batch classes can return an iterator to process large volume of data that are not directly queried from database/sobjects. These iterators can be classes implementing Iterable interface or standard apex data strucutres like list. In above example we are getting a list of animals and details of individual animal from an API hosted in Heroku. It is used by Salesforce for trailhead modules. Here "baseUrl/animals" gives a list of animals and "baseUrl/animals/animalId" gives details of a single animal.

Suppose the API to get list of animals gives back 1000 animal IDs. Then we cannot get details of all animals individually using single transaction. This is because currently governor limit on number of callouts in one transaction is 100. So we need to split it into multiple transactions. Best way to do this is to use apex batch. Here we use start method to make an API call to get list of all animals. Then we will process the result and finally start method will return a list of all animal Ids to execute method. Then because it is a batch, Salesforce will call execute method multiple times with a subset of total animal list each time using the batch size we specify while starting the batch. This will help us to stay within governor limits if we use correct batch size (Something below 100 in our case so that we can stay within 100 callouts per transaction limit).


How to use

  • Make sure that you are adding https://th-apex-http-callout.herokuapp.com/ in remote site settings to allow callouts to this URL.
  • Create an apex class with name "IteratorExampleBatch" and copy paste the code from above
  • Execute the batch using "Developer Console" => "Execute Anonymous" option. You can use Database.executeBatch(new IteratorExampleBatch(), 10); to execute batch with a batch size of 10

How to use lightning data service in lightning web components(LWC)

Lightning data service allows you to cache and share data between lightning components using browser cache. Lightning framework takes care of managing cached data in browser. You do not need to write any code to interact with browser cache. There is no need to write any apex code also. Since the data is coming from client/browser cache, you can access data quickly if it is cached already. Lightning data service is built on top of Salesforce user interface API.

Use cases

  • Create simple forms that can create/view/edit single record.
  • lightning-record-form can be used to display data from all fields in standard page layout.
  • If you are building a component to use in a record details page and the component only updates the fields in the same record, it is highly recommended to use lightning data service, since Salesforce keeps data in sync with details page automatically.

<template>
<lightning-card title="Lightning Data Service">
<p class="slds-p-horizontal_small">
<lightning-input type="checkbox" label="Checkbox Field" name="input1" onchange={handleFieldChange} checked={checkboxValue}></lightning-input>
</p>
</lightning-card>
</template>
import { LightningElement, wire, api } from 'lwc';
import { getRecord, getFieldValue, updateRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import ID_FIELD from '@salesforce/schema/Contact.Id';
import MY_CHECKBOX_FIELD from '@salesforce/schema/Contact.My_Checkbox_Field__c';
export default class LwcDataserviceComponentt extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId',
fields: [ID_FIELD, MY_CHECKBOX_FIELD],
optionalFields: []
})
contact;
get checkboxValue() {
return getFieldValue(this.contact.data, MY_CHECKBOX_FIELD);
}
handleFieldChange(event) {
const fields = {};
fields[ID_FIELD.fieldApiName] = this.recordId;
fields[MY_CHECKBOX_FIELD.fieldApiName] = event.target.checked;
const contactToUpdate = { fields };
this.updateContact(contactToUpdate);
}
updateContact(contactToUpdate) {
updateRecord(contactToUpdate)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'checkbox Updated',
variant: 'success'
})
);
})
.catch(error => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error occurred while updating checkbox',
message: error.body.message,
variant: 'error'
})
);
});
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>47.0</apiVersion>
<isExposed>true</isExposed>
<description>This component demonstrates using lightning data service in lwc components.</description>
<targets>
<target>lightning__RecordPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<objects>
<object>Contact</object>
</objects>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>

Explanation

You can use "getRecord" and "updateRecord" methods from "lightning/uiRecordApi" to get record and update record from lightning data service respectively. After getting a record you need to wire it to a variable to make it available in UI. Then you need to use "getFieldValue" method and create a getter method to make the field available in the UI.

If we go step by step in the flow, below actions happen in a sequence when the component is loaded and value is changed.

  1. Component gets loaded in a record details page and salesforce passes "recordId" attribute is passed in to the component
  2. @wire annotation uses "getRecord" to get data from Salesforce server using lightning data service
  3. This data is made available to HTML through "checkboxValue" getter method. At this point if the data in contact record is checked, it will be dispalyed in UI
  4. Say user changed the value of checkbox by clicking it. "handleFieldChange" method is called. This method creates an object to update and passes to "updateContact" method
  5. "updateContact" method uses "updateRecord" method from lightning data service to send this data to Salesforce server and update
  6. Based on success or failure, a toast message is shown by firing an eveng


How to use

  • This code assumes that you have a checkbox field in your contact object with API name "My_Checkbox_Field__c"
  • Create an LWC component with name "lwcDataserviceComponent". Copy HTML, JS and XML code above to relevant files
  • Once the component is saved, you should be able to see it in app builder interface for contact object. This is because we have made it available through the metadata file of lightning component
  • Drag and drop the component to contact page and activate it
  • If you have the same field in record details section, you will be able to see that getting updated in real time as you update the checkbox in the lwc component.