Saturday, 18 January 2020

Going GUI over the Salesforce CLI Part 2

Going GUI over the Salesforce CLI Part 2


In part 1 of this series I introduced my Salesforce CLI GUI with some basic commands. In this instalment I’ll cover some of the Electron specifics and add a couple of commands.


Electron is an open source framework for building cross platform desktop applications with JavaScript, HTML and CSS. The Chromium rendering engine handles the UI and the logic is managed by the Node JS runtime. I found this particularly attractive as I spend  lot of time these days writing code for Node - wrappers around the command line, for example, or plugins for the Salesforce CLI. I’m also keen to do as much in JavaScript as I can as it helps my Lightning Web Components development too.

An Electron application has a main process and a number of renderer processes. The main process creates the web pages that make up the application UI, and each application has exactly one main process. Each page that is created has its own renderer process to manage the page. Each renderer process only knows about the web page it is managing and is isolated from the other pages.

The renderer process can’t access the operating system APIs - they have to request that the main process does this on their behalf. 

CLI GUI Main Process

The main process for my CLI GUI loads up the JSON file that configures the available commands, runs a couple of CLI commands to determine if the default user and default dev hub user have been set. It then registers a callback for the ready event of the application, which means that it is fully launched and ready to display the user interface:

app.on('ready', () => {
    mainWindow=new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
    let paramDir=process.argv[2];
    if (paramDir!==undefined) {

mainWindow.webContents.loadURL(`file://${__dirname}/home.html`); windows.add(mainWindow); });

The ready handler creates a new BrowserWindow instance, specifying that node integration should be enabled for the renderer process that manages the page. It then loads the home.html page, aka the GUI home page:

CLI GUI Home Page Renderer

The Node JavaScript behind this page (home.js in the repo) gains access to the main process via the following imports:

const { remote, ipcRenderer } = require('electron');
const mainProcess = remote.require('./main.js');

Remote gives access to a lot of the modules available in the main process (essentially proxying) and mainProcess provides access to the functions and properties of the main process instance that created the renderer. The renderer then gets a reference to its window so that it can output the dynamic content:

const currentWindow = remote.getCurrentWindow();

It then iterates the command groups, creating a Salesforce Lightning Design System tab for each one, then iterates the commands inside the group, adding buttons for each of those. There’s a fair bit of code around that to set the various attributes, so a cut down version is shown here:

for (let group of mainProcess.commands.groups) {
    if (0===count) {
    let tabsEle=document.createElement('li');'tab-'+;'tab-' + + '-link';


    const tabContainer=document.querySelector('#tabs');
    let contentEle=document.createElement('div');
    contentEle.classList.add('slds-' + (0===count?'show':'hide'));
    contentEle.setAttribute('role', 'tabpanel');

    let gridEle=document.createElement('div');

    const tabsContentContainer=document.querySelector('#tab-contents');

    for (let command of group.commands) {

        let colEle=document.createElement('div');

        let colButEle=document.createElement('button'); + '-btn';
colEle.appendChild(colButEle); gridEle.appendChild(colEle); } }

This allows the commands to be dynamically generated based on configuration, rather than having a hardcoded set that are the same for everyone in the world and requiring code changes to add or remove commands. Of course there is code specific to each of the commands, but there are a lot of similarities between the commands which allows a lot of code re-use.

Note that I’m using the DOM API to add the elements, rather than HTML snippets, partly because it is slightly faster to render, but mostly because while it takes longer to write the initial version, it’s much easier to maintain going forward.

Note also that the buttons and tabs have dynamically generated ids, based on the command names. This allows me to add the event handlers for when a user clicks on a tab or a button:

for (let group of mainProcess.commands.groups) {'#tab-' + + '-link');'click', () => {
    });'#tab-' +;
    group.content=document.querySelector('#tab-' + + '-content');

    for (let command of group.commands) {
        command.button=document.querySelector('#' + + '-btn');
        command.button.addEventListener('click', () => {
            mainProcess.createWindow('command.html', 900, 1200, 10, 10, {command: command, dir: process.cwd()});

The more interesting handler is that for the commands - this invokes a method from the main process to open a new window, which again has been cut down to the salient points:

const createWindow = exports.createWindow = (page, height, width, x, y, params) => {
    let newWindow = new BrowserWindow({ x, y, show: false ,
        width: width, 
        height: height,
        webPreferences: {
            nodeIntegration: true
      newWindow.loadURL(`file://${__dirname}/` + page);
      newWindow.once('ready-to-show', () => {
          if (params!==undefined) {
              newWindow.webContents.send('params', params);
      newWindow.on('close', (event) => {

The new window is created much like the home window, and loads the page - command.html in this example. Unlike the home page, once the new window receives the ready-to-show event, a handler sends any additional parameters passed to this method to the window - in this case the command that needs to be exposed and the current directory that it will be run in. There’s also a close handler that destroys the window, cleaning up the renderer process.

New Commands

The latest repo code contains a couple of new commands in a Debugging group. As before, I’ve tested this on MacOS and Windows 10.

List Log Files

Choose the username/alias for the org whose log files you are interested in:

Clicking on the ‘List’ button pulls back brief details of the available log files:

Get Log File

To retrieve the contents of a specific log file, first find out which ones are available from a specific org:

Then select the specific file from the dropdown:

And click the 'Get' button to display the contents:

Related Posts


Sunday, 12 January 2020

SalesforceWay Podcast

SalesforceWay Podcast

Mid 2019, Xi Xiao from Finland reached out to ask me if I’d appear on his podcast, SalesforceWay. I’ve done a podcast or two in the past, but not for some time, so I accepted with gusto. As is so often the case, I had a stack of work that I was already behind on and some half-completed community initiatives that needed some focus. Luckily Xi was a patient man and shortly (a quarter of a year or so!) afterwards we recorded the episode

Anyone who has been following my blog over the last couple of years will know that I’m a big fan of the Salesforce CLI, so this seemed like a great topic to talk about. For those who aren’t familiar with the podcast format, I always feel it’s kind of a cross between an interview and a collection of war stories. As a rule they are intended to enlighten rather than train, involving a conversation around a topic and pointing the listener at where they might find out more information.

While my episode is clearly the one you should listen to first, there are a ton of great episodes available, so don’t stop at one! There are also more on the way - I know this as I recommended a few of the guests. Xi is always looking for more, so if you have a topic that you think other Salesforce developers would be interested in, reach out to Xi, or if you are shy then get in touch with me via the usual channels (or the comments section of this post) and I’ll arrange an introduction. You won’t regret it - it’s a lot of fun!

Related Posts

Sunday, 29 December 2019

Going GUI over the Salesforce CLI

Going GUI over the Salesforce CLI


The Salesforce CLI has been around since 2016, and its predecessor (the CLI) is even older, debuting at Dreamforce 2013, and both of these have been making developers lives better ever since. While these tools aren’t just for devs, as a fair amount of admin work can be carried out using them, the command line doesn’t always have broad appeal to those who don’t spend most of their working lives writing code or manually executing commands. 

Now the command line isn’t the only way to access some (but not necessarily all) Salesforce CLI commands - the Salesforce VS Code extensions provide access to a large number through the command palette, and it’s relatively straightforward to add simple use cases by configuring custom tasks. That said, it’s a bit of a sledgehammer to crack a nut, as it’s a full fledged IDE being used to display a couple of dialogs and some output, and there’s a fair bit of installation/configuration required which can quickly take people out of their comfort zone.

Every time I heard about a need for a GUI for the CLI, I’d think to myself that it couldn’t be that hard to build one. A lot of it is around wrapping the CLI commands in Node scripts and presenting the output, something I’ve been doing for years. Typically what happens next is I keep putting off doing anything about it, someone else gets there before me, and I get annoyed at my lack of action. This time I was determined things would be different, and earlier in 2019 this collided with my desire to learn to build cross platform apps using Electron, so I finally got got going on YASP (Yet Another Side Project). 

This post is a light touch on the technical side of things as there’s a fair bit to cover and I don’t want to put people off using the GUI because of too much detail. For anyone interested in knowing more about how the internals work, rest assured there will be more posts. Many more. Far more than you expect or want.

The GUI App

The GUI is built using Electron, a combination of Node JS and Chromium (essentially open source Chrome). If you can write HTML and JavaScript you can build an Electron app - there’s obviously a bunch of specifics to learn, but I found it a very interesting experience. I have intentionally not used any kind of additional framework, instead I’m manipulating the DOM of the various windows using vanilla JavaScript.

Everything in Electron starts with the main process - this manages the application lifecycle and interacts with the operating system. The main process is responsible for creating the windows that the user interacts with, and the main window for my application looks like this:

The body of the page is a set of tabs, each of which have a grid of buttons, each button associated with a Salesforce CLI command. The tabs and commands are configurable via the app/shared/commands.js file included with the application. At present this is a smallish set of commands, but others are pretty close to ready and will be added in the coming weeks. The file embedded in the app will also be the starting point for the app, but users will be able create a local version to tailor the tabs and commands to their exact requirements. 

Clicking a command button opens another window to configure and execute the command:

The page for any command is constructed dynamically based on the configuration from the app/shared/commands.js file, which I’ll cover in a future blog post.  What it does mean is that, as long as the parameters for the command are either simple or already handled, adding a new command is simply a matter of adding a stanza to the commands.js file. 

As this is intended to be helpful to those who aren’t that comfortable with the command line, the command to be executed is displayed as the parameter details are entered. In fact there’s no need to execute a command from the app, a user can simply construct the command then copy and paste it to a command line session if they so desire.

The page header has the following buttons:

Help - this opens up the Help page for the command, with the Overview text again coming from the app/shared/commands.js file, and the Command Help coming directly from the Salesforce CLI.

Change Directory - after following the instructions in the README file, the GUI app starts in the cloned repository directory, which may not be where commands need to be executed from (to pick up a project specific sfdx-project.json file, for example). Clicking this button allows the user to change the working directory. If the directory is changed from the main page, this will apply to all commands executed thereafter, whereas if it is changed from a command page hen it only applies to that command window. The current working directory is always displayed on the footer of a window.

Show Log - when you execute a command the output is shown in a Log modal. If you close this you can re-open it at any point in time by clicking the Show Log button.

Supported Commands

For the initial release, the following commands are supported:

  • Login to Org - authenticate a user for a Salesforce instance
  • Logout of Org - clear a previously authenticated user - always do this for orgs containing real data
  • Default Username - set the default user for future commands, either in a specific project directory or globally. 
    Note that the GUI takes this is a default value that the user may with to change, so it will simply be pre-selected in the Org dropdown
  • Default Dev Hub - set the default developer hub user for future commands, either in a specific project directory or globally. 
    Again, the GUI takes this as a default value that can be overwritten.
  • Open Org - this is the command I use the most by far as with my various duties as CTO of BrightGen ad myriad side projects, I’m forever needing to be in a different org.
  • Create Scratch Org - create a Salesforce DX scratch org
  • Delete Scratch Org - delete a scratch org prior to its scheduled expiry time

I have more commands that are pretty close to ready, so there will be more added over the first weeks of 2020, assuming time allows.

All of the commands implicitly add a —json switch so that the output can be parsed programmatically - this does mean that you won’t see any output until the command is complete, so patience is required.

Caching Org Names

Any command that requires a user or dev hub user provides a datalist (a dropdown that you can also search in) with options based on the required org type (scratch, dev hub, any). The Salesforce CLI command to retrieve the orgs the user is currently authenticated against takes quite a while if you are authenticated against a lot of orgs, so this is run at startup and the results cached in a file with any authentication tokens removed. Any commands which change the authenticated orgs (e.g. creating a scratch org) will require the org cache to be refreshed, which can take a few moments. If you carry out any commands outside of the GUI which add or remove orgs, simply click the Refresh Orgs button on the main page and this will update the cached information.

Getting Started

Obviously you need to have the Salesforce CLI installed before you can work with the GUI. Assuming you have this, you can simply clone the repository, execute npm install to install the dependencies, then execute npm run start to fire up the GUI and away you go. The dependency installation takes a little while if you are on a slow internet connection, as Electron is touch on the large side.

Note that I’ve tested the GUI on MacOS Catalina and Windows 10. It might work on other OS and it might not. Caveat emptor.

Related Posts


Saturday, 14 December 2019

Install Packages in Trailhead Playgrounds without Password Resets

Install Packages in Trailhead Playgrounds without Password Resets

The Credentials Conundrum

A number of Trailhead modules require you to install a package into a playground. This can present a challenge, as the package link provided is the generic version that starts with, requiring you to login with the org credentials. Org credentials which as a rule you don’t know, as you just open a playground by clicking on a link in the module itself:


The upshot is you need to reset your password to get something you can login with - this crops up so often that it has it’s own module.

The Package Link

Picking a Trailhead module that requires a package at random, Quick Start: Salesforce Connect, right clicking the link and copying the address gives us:

As mentioned earlier, this points to, but it doesn’t have to. The part of the URL that drives the installation of the package is :


where packaging/installPackage.apexp is the setup page for installing a package, and p0=04tE00000001aqG identifies the package to be installed, in this case  04tE00000001aqG.

Applying the Package Link to the Playground

Once you have the package link, this can be applied to any org that you are logged into. Continuing the example from above, here’s a Trailhead playground that I prepared earlier:

From the URL bar, I can see tyhe domain name for the org is: Replacing from the package link determined above gives me:

Changing the URL in the browser takes me to the installation page and I can install the package without knowing anything about the org password.

A CLI Tangent

It wouldn’t be a Bob Buzzard Blog post without pimping the CLI. Not particularly applicable to this scenario as you need to authenticate the org using the CLI so you’d have gone through the password reset already, but never any harm in learning about the power of the command line :)

If you have the package id, you can install directly from the command line using the following command:

sfdx force:package:install -r -p 04tE00000001aqG -w 10

The -r switch says not to ask for confirmation (useful when scripting an installation to run unattended as part of a CI setup) and -w 10 says to wait for up to 10 minutes for the package to install. 


Saturday, 30 November 2019

Dreamforce 2019

Dreamforce 2019

Updated 01/12/2019 to add that Evergreen is in closed developer preview in Spring 20.

It’s now over a week since Dreamforce 2019 officially finished, the jet lag is starting to fade and those afflicted by show fever are coming to terms with the fact that a lot of what they saw isn’t going to be available for a while.


Marc Benioff’s keynote was somewhat different from previous years, reflecting the sheer breadth of the Salesforce product set. Instead of lengthy demonstrations of specific clouds, we got quick snippets and pointed at the keynote for the cloud in question. How long before it’s just a page of short links that we read through together?

There was an additional demonstration, when a protestor stood up and started reading a speech that only those around him could hear.

This was clearly expected, as Benioff didn’t miss a beat and told him he had 30 seconds and a timer appeared on screen. This would have been the time to grab the mike and make a few killer points, but the plucky protestor simply continued reading from his script without amplification in a room that holds around 15,000 people. Definitely a missed opportunity in my opinion.


There are some cool features on their way, especially for developers, and below are three that really caught my attention.

1. Evergreen

The coolest announcement was Evergreen. (As is traditional for Salesforce, this term has already been in use for a while, originally denoting a CPQ subscription with no end date).From a developer perspective, it now means serverless functions, written in open programming languages, invoked from Apex, /Flows or triggered by Events. This kind of thing has always been possible (and Mick Wheeler's talk on Microservices inside Salesforce with Platform Events and Change Data Capture demonstrated exactly this) but the secret sauce for Evergreen is (per the Salesforce blog post) :

Evergreen is a seamless part of the Salesforce platform and no extra authentication or networking setup is required. 

The demo that I was given in the Trailhead zone made it look very simple (no surprise there), with Node microservice code in a subdirectory of the force-app/main/default directory, everything configured through Salesforce setup, and the deployment handled by Salesforce tooling. A fair few questions were met with the response “we haven’t decided exactly how that will be done yet”, but as an early preview it was quite impressive. Evergreen goes into closed developer preview in Spring 20. Pre-release orgs should be available in a couple of weeks, but often these don’t have all features enabled from the word go, so we might end up having to wait until the release lands around February. UPDATE 01/12/2019 - I’ve been informed by product management that Evergreen will be in closed developer preview in Spring 20, which means you’d have to be nominated to take part - contact Salesforce if you think you have a good use case, and if you don’t, get used to waiting!

Evergreen feels to me like an admission that the Salesforce platform has been taken about as far as it can be. Those of us working with large and complex implementations have found ourselves spending more of our time battling governor limits (particularly CPU time) and while going asynchronous can certainly help, that usually requires a degree of orchestration and also brings limits of it’s own. By making Evergreen a seamless part of Salesforce, where developers really don’t have to care that much about exactly where their code runs, it sounds like the product team have given us a good mechanism to scale. Of course we haven’t seen the pricing yet, and there’s probably an element of smoke and mirrors about the demo, but so far it looks very good.

Andy Fawcett’s influence was clearly making itself felt here, as these functions will also be packageable by ISVs!

2. Data Mask

As part of the ongoing privacy wars, Salesforce will soon have native protection of data copied from production when a sandbox is refreshed, via Data Mask. This provides three options for data protection:

  1. Anonymization (which we used to call scrambling), where the data is converted to meaningless values.
  2. Pseudonymization, where the field is left with a readable, indicative value, but unrelated to the original value. For example, if the field is a phone number, the real data will be replaced with something that looks very much like a valid phone number, but isn’t. 
  3. Deletion, where the contents of the field are removed.

This is another area where we’ve been able to do this kind of thing for a while, but never completely. For example, we could easily run an apex class after refresh to protect the data, but if field history tracking is enabled then we’d still end up being able to view the original values. We can turn off field history tracking via the metadata API, but then the original data is unprotected for a short while, which means we have to stop everyone logging in until we’ve taken care of all this, which is another brick in the wall. Something that takes care of all this under the hood is a great improvement.

According to the Salesforce Developers blog post, Data Mask will be generally available “next month” which at the time of writing means December 2019.

3. Open Source Lightning Base Components

50 odd of the Lightning Base Components have been open sourced, with more to come. Initially this will be interesting to understand the implementation details and see if we are anywhere near the standard approach. The Github repo makes it clear that contributions are not welcome right now, but I’m sure in the fullness of time they’ll open up the firehose and have to deal with a torrent of pull requests.

If you don’t see your favourite component in the repo, that means it isn’t open sourced yet. Don’t panic, it’s a work in progress.

There’s  a Trailhead for that

In the continuing story of Trailhead eating the world, the developer and admin keynotes had a call to action in the form of a trailmix:

One More Thing

The theatre sessions weren’t recorded this year, just the breakout sessions in Moscone West, so if you presented a theatre session, you can replay it for your local developer group without too many people having a sense of deja vu. 

Related Posts


Thursday, 7 November 2019

Dreamforce - It's a Marathon, Not a Sprint

Dreamforce - It’s a Marathon, Not a Sprint

At the time of writing (Nov 7th 2019) Dreamforce is 12 days away. This year will be my 10th consecutive year attending, which doesn’t seem possible but here we are. In spite of my best efforts, I have learned a few things over the years which may be of use to those heading for their first event.

Packing List

Comfortable Shoes

While this feelst like a hackneyed trope, it really is one of the best pieces of advice, especially if you are exhibiting. You’ll be on your feet a huge amount of time, especially once you factor in breakfast sessions and parties, so make sure you aren’t in agony from a couple of hours in. If you need to put on smart shoes (to present, for example), pack them in your rucksack and switch into them only for as long as you need them.

Battery Charger

While you might think your phone has plenty of charge, Dreamforce days are long days. Yes there are charging points, but do you really want to stand there for 45 minutes just charging your phone. It’s also a great way to make new friends if you have some charge to spare!

Space in your Case

You will pick up swag, water bottles and your Dreamforce backpack. Make sure you have enough room to get them home. Also, remember that when picking up your Dreamforce backpack, you may now have two backpacks to manhandle. This is surprisingly easy to forget and it gets real old real quick fighting your luggage all the time.

Exhibiting is Tough

The first year I went to Dreamforce was, like so much of my career, just dumb luck. BrightGen had decided to take a stand, and one of the sales team who was due work the stand took up an offer elsewhere. So a couple of weeks out I was suddenly going to Dreamforce, but on a Booth Staff ticket. This ticket allows you entry to the expo and keynote broadcast rooms and that’s about it, so any ideas I had about attending sessions were not to be. Not that I would have had any spare time anyway, as for some reason everyone was really interested in talking to us about service management contracts even though we were the other side of the world with an 8 hour time difference.

Working a stand is a hard job at Dreamforce, as it’s a multi-day event. Early starts and late finishes are the norm and you have to make sure you are ready to pitch your wares at a moments notice. Don’t underestimate the cumulative effect of four long days on your feet, typically followed by Salesforce or customer events, so you’re still on duty. This is one trip I really wouldn’t want to recreate.

Fitting in Sessions is Tough

One piece of advice I always give to those attending for the first time - don’t sign up for too many sessions, as you won’t get to them. However many you think you’ll be able to make, it will be less, for a variety of reasons including the following.

Moving Buildings Takes Time

Even if it seems like they are really close to each other, such as moving from Moscone West to the Hilton, which as the crow flies is about 50 yards. However, you have to get out of your breakout room, which will take a couple of minutes, longer if you have questions for the presenter. Then you’ll have to get down one or two escalators - if multiple sessions or a keynote have kicked out at this time, you’ll be queuing there for a while. Getting out onto the street from ground level is a snip, but then you have to cross the road. If it’s anything other than early in the morning you’ll find a few thousand people with the same idea, so you’ll have to wait a while.Then you’ll funnel in to the Hilton, figure out where the room is and if you are early enough, join the queue of registered attendees. If you haven’t allowed enough time you’ll find that general admission has been opened up and there are no seats left. Suddenly your planned-to-the-second agenda is blown to pieces and it’s just after lunch on the first day.

If the buildings are a long way apart (Rincon Center to Moscone, for example) it has taken me 30 minutes to get between them around lunchtime. Don’t forget that you are in the middle of a big city, so you have all the standard delays that come with that plus Dreamforce traffic on top.

You Will See People You Know

Even if everything else goes to plan, you’ll bump into someone you know, either in person or online, and stop to chat with them. When you finish chatting you’ll realise it is now 10 minutes into your next session, which is in a different building to the one you’ve been chatting in. This happens to me all the time.

Keynotes Are Busy

Marc Benioff’s keynote is crazy busy, and if you are planning to attend that in person then you really don’t want have anything else to do for at least an hour before. The queues will be huge, and the metal detectors won’t help.

Anything involving American politics will be popular - Hillary Clinton and Michelle Obama both generated queues around the block, which made any kind of movement, towards or away from the keynote, very difficult. I expect Barack Obama to be like this but multiplied several times.

Getting out of the main keynote rooms takes ages, as there are usually 5,000-15,000 people trying to funnel through 4 doors onto a few escalators. If you want to get anywhere quickly, either leave a few minutes early or wait 10-15 minutes.

The Trailhead Zone is Awesome

Once you get in there you won’t want to leave. But you’ll need to, in order to go to the next session that you’ve tried to cram in. So leave yourself some time to explore. I spend a lot of my time here.

Get to the Trailhead Zone Early

Most of the hands-on areas fill up really quickly, so if you don’t want to spend more time queueing, get there as soon as it opens. It also means that there will be plenty of swag once you’ve completed your challenge/trail/quest/whatever it is called this year. The lines for things like spinning the admin wheel of fortune, headshots, t-shirts etc are usually short or empty, so if you move quickly you can cover a lot of attractions in a short time.

And Stay There

As I mentioned earlier, I spend a lot of my time here. Not particularly for the hands on side of things, but for the theatre sessions. I usually prefer these to the full on breakout sessions as they are more bite sized and I can get a quick introduction to something I haven’t worked on before and if it piques my interest, I’ll then go off somewhere and learn more about it. There’s always a talk going on somewhere and you can just pitch up and start listening if something takes your fancy.

Don’t Forget your Badge and Lanyard

10 years ago you could get around with just the badge in your wallet, especially for the after parties. That’s no longer the case and you typically need the whole thing everywhere. Remember that you don’t have to wear it all the time on the street to advertise the fact that you are a tourist. 

You aren’t out with your Friends

By this I don’t mean that people are unfriendly or unhelpful, quite the reverse given the well-publicised Ohana community spirit. What I mean is that you aren’t out on the lash with a bunch of mates who will think it hilarious if you get smashed and cause a scene. You’ll be among colleagues, customers and partners who will expect you to behave in a professional manner. It’s very easy to undo a huge amount of work and hard-won goodwill with a single drunken episode, so always remember you need to stay in control.  Even if you don’t care about the effects on your own reputation and career, nobody else is there to watch you make an idiot of yourself.

It’s a Marathon not a Sprint

Dreamforce is four days, so don’t kill yourself trying to do everything on day one. This goes double for the parties - you won’t get much return on the investment for your trip if you are hungover all the time, and this will definitely reduce the sessions you can make it to (or stick around in!). If the lines are long in the Trailhead zone, get an early night and turn up first thing the the next morning - you’ll be glad you did.

Don’t feel bad about taking a time out whenever you need to. While there are mindfulness/quiet zones at the event, getting away from it all for a little while can be a nice change of pace. I usually walk down to the Embarcadero area and spend a few minutes looking at the bridges, boats and water.

Above all, have fun - don’t get so hung up on trying to do everything that you don’t enjoy the experience.

Don’t Miss Sessions

  • Mine
    I’m on at the Developer Theatre at 3:45 on Wednesday 19th, taking about UI Testing with Selenium and NodeJS.

  • Marc Benioff’s Keynote
    All the big announcements come at this keynote - there are plenty of overflow rooms if you can’t get there in person, and it will be streamed live.

  • Developer Keynote
    If you are a developer you’ll definitely want to see this one. Arrive early for decent seats.

  • Trailhead Keynote
    Always a riot and one of the loudest keynotes you’ll come across. Sit down the front to be deafened by excited MVPs! 



Wednesday, 30 October 2019

Auto-completing a Signature Capture Flow

Auto-completing a Signature Capture Flow


One of the requests I’ve received from a couple of people around embedding Signature Capture is around automatically navigating once the signature has been captured. One request from the POV of users forgetting to click the Next/Finish button after saving the signature image, and one from the POV of not letting the user navigate further until they have captured a signature image. Luckily both of these can be handled via the same mechanism.

Taking Over the Flow Footer

Aura components that implement the lightning:availableForFlowScreens interface can manage navigation for the screen element they are embedded in. However, you do have to configure the screen so that the footer is hidden:

Once this is done, you have to control navigation via your aura component or the user will be stuck on that flow screen component for eternity.

Handling the Navigation

A lightning component that implements the lightning:availableForFlowScreens interface automatically receives the v.availableActions attribute, which lists the available navigation actions (prev/next etc). This is a collection of strings documented here. Executing a navigation action is as simple as accessing another implicit attribute, v.navigateFlow, and executing this with the desired action:

let navigate=component.get("v.navigateFlow");

Signature Capture With Navigation

In order to carry out the flow-specific navigation I’ve created a new component (SigCapFlowWithFinish). The component wraps an embedded SignatureCapture component and replicates its attributes so that these can be exposed in the flow builder. It also provides a handler for the SignatureCapturedEvt, fired when the user saves the signature image:

<aura:handler event="BGSIGCAP:SignatureCapturedEvt" action="{!c.handleCaptured}"/>

The handler for this event iterates the available actions and if it finds a next or previous, executes that. (Note that you can’t have both next and previous, so there’s no need to worry about ordering).

let flowAction=null;
let availableActions=component.get('v.availableActions');
for (let idx=0; idx<availableActions.length && null==flowAction; idx++) {
    let availAction=availableActions[idx];
    if ('NEXT'==availAction) {
    else if ('FINISH'==availAction) {

if (null!=flowAction) {
    let navigate=component.get("v.navigateFlow");

So when the user saves the signature, the flow auto-finishes or moves to the next element, and until they save the signature they can’t move forward. Here’s a quick video showing this for a flow launched from a contact record:


Share the Code, Share the Love 

You can find the component and it’s associated flow in the Signature Capture Samples repository, and here are direct links to the flow and component.

Related Posts