In Lightning Aura components one can use force:hasSObjectName to get access to an attribute sObjectName. In Lightning Web components one uses @api objectApiName. Except neither of those work in Communities.

I had an itch to scratch. After using Field Trip (which I like a lot) to determine unused fields, the team managing the external Informatica integration claimed they would need weeks to ensure none of the fields are used in any of their (hundreds) of pipelines.

My first reaction (OK, the second, first one isn't PC) was: Let's go after the source code and just use an editor of choice to do a find in files. Turns out: not so fast. The source export offered by the team was a zip file with an elaborate directory structure containing, tada, zip files. So each of the pipes would need multiple zip operations.

I needed a tool that would start in a directory with a bunch of zip files, unpack them all. Check for zip files in the unpacked result, unzip these and repeat. Once done, take a list of strings and search for occurrences of those and generate a report which shows the files containing these strings

I created findstring, a command line tool that takes a directory as starting point unzips what can be unzipped (optional) and searches for the occurrence of strings provided in a text file.

Initially I contemplated to render the output as XML, so the final report could be designed in whatever fashion using XSLT. However following KISS, I ended up using Markdown. I might add the XML option later on.

The key piece of the tool is recursion (until you stack overflow ;-) ). Reading a directory and dive into directories found. I could have avoided that using Guava and its fileTraverser, but I like some Inception style coding. The key piece is this:

In its current form the utility will check for strings in any file short of zip. Zip gets unpacked and the result checked. When your directory contains binary files (e.g. images) it will still look for the string occurrence inside. File extension filters might be a future enhancement (share your opinion).

Files are read into memory. So if your directory contains huge files, you will blow your heap. Source code files hardly pose an issue, so the approach worked for me. Alternatively a scanner could be used, should the need arise.

UI-licious to the rescue. UI-licious is a testing framework for UI tests. They use a simple JavaScript syntax to provide testing and a rather clever addressing of elements. Other than Selenium, they don't rely on CSS selectors or XPath expressions (You still can use those).

To be very clear: A UI level testing library is not a replacement for proper unit testing. UI-licious has two use cases here: top of the pyramid UI testing and spotting UI level regressions. To learn more about the "testing pyramid", check out Martin Fowler's essay.

To give it a try I created 2 components with identical functionality: one in Aura, one as LWC. The components show a dialog where you can pick values for radio buttons. Shi Ling, the CEO provided the test script (the login subroutine omitted for brevity):

When you pick a Lightning Template you will have a build in menu navigation. This works well if all menu items are meant for all users (no assignment of audience), but breaks down for more sophisticated or programmatic navigation.

On first view the Lightning navigation service (available in Aura or LWC) seems like the answer. However on inspection of lightning-navigation you find as supported experiences only Lightning Experience and Salesforce mobile app, Communities are missing.

Digging a little deeper and checking the Page Reference Types, you will find "limited support for Communities". I tested it out, here are my findings:

The documentation is accurate. What is stated as working works, what is stated as not supported in communities does not work.

The painfully missing piece is standard__component which would allow to navigate to a custom lightning component. It is the only component that supports state (more on that later)

Navigate to standard__objectPage opens the list/page layout based on the user's profile. When you specify. actionName="new", the standard object detail page will open. It will not use an eventual define new button overwrite

Works as specified: standard__recordPage, standard__knowledgeArticlePage

Doesn't work: standard__webPage

None of the navigation working in communities supports the state properties

The most interesting navigation in communities is standard__namedPage. Beside the predefined default pages "Home","Account management", "Contact Support", "Error", "Top Articles" and "Topic Catalog", it supports "Custom Pages". In other words: any of the pages you have created in your community. So the missing standard_component can be mitigated by embedding it into a custom page. Keep in mind: the pageName property is the URL, not the name.

As mentioned above, the state property gets ignored, dropped without an error when used with any of the working navigation items. The remedy for that is to use the session store. An Aura code snippet would looks like this:

I left the state in the pageReference JSON object to show that it doesn't harm. The navService component is defined as <lightning:navigation aura:id="navService"/> in Aura. On the receiving end you use:

Whatever template system you use, you will end up with show/hide logic based on your data's values. In Aura components you have an expression language (reminded me of JSF), in LWC external (in your JavaScript class) boolean values or functions.

A common interaction pattern, similar to the Salesforce default behavior when you have more than one record type available, is to show a pre-selection (which record type), a main selection (required data) and (eventually) a post-selection (what's next?).

In a lightning web component you can handle that easily using if:true|false inside your html template.

But what if the sections are quite lengthy? Maintaining the HTML template can get messy. Enter the render() method. In LWC this method doesn't to the actual rendering, but determines what template to use to render the component.

There are a few simple rules:

You need to import your template into your JavaScript file

The call to render() must return the imported variable (see example below)

You can make the computation dependent on anything inside the class

You can't assemble the template in memory as a String, it will throw you an error

When developing lightning components using the Aura Framework you could use a series of global value providers that give you access to various data sets: $ContentAsset, $Labels, $Locale, and $Resource.

While this convenient, it pollutes the global name space and it a very proprietary (albeit popular at its time) way to provide information. LWC fixes this in a very standard compliant way. This became possible thanks to the new capabilities in the JavaScript ES6 standard.

In LWC all information provided by Salesforce gets added using ES6 import statements from the @salesforce name space. While that syntax is new to Salesforce developers, it is old news for the rest of JavaScript land. So here you go:

A computing principle, very much the anathema to Google and Facebook, is Data Frugality, storing only what you actually need. It is the data equivalent to coders' YAGNI principle. Latest since GDPR it got center stage attention.

Find fields that don't have any data. You can use tools like Field Trip to achieve that

Verify that these fields are not "about to be used", but "really obsolete"

Add all the fields that did have some data left over, but unused now

Add fields that contain data legal told you to get rid off

The absolute standard approach, of any consultant I have encountered, is to fire up an Excel sheet and track all fields in a list, capture insights in the remarks column and have another column that indicates can be deleted Status. Something like Yes,No,Investigating or "Call Paul to clarify". I would be surprised if there's a different approach in the wild (in theory there are).

In a current project the consultant neatly created one sheet (that's the page, not the file) per object, labeled with the object name, containing rows for all custom fields. Then the team went off to investigate. In result they identified more than one thousand fields to be deleted.

Now to actually get rid of the fields, you could outsource some manual labor to either go into you org or use Copy-Paste to create a destructivechanges.xml package file for use with the Salesforce ANT tool.

In any case: the probability that there will be errors in transferring is approximately 100%. The business owner will point to: I signed off that spreadsheet and not that XML file! Finger pointing commencing.

Our goal is to distribute happy soup artifacts into packages. In this installment we setup the directory structure for that. Sticking to a clear structure makes it easier to get a step closer to package Nirvana step by step.

Let me run through some of the considerations:

I'll keep all packages inside a single directory structure. Name the root after your org. What might pose a challenge is to name it sfdx - too close to that hidden directory .sfdx that does exist in your home directory and might exist in the project directories

You could keep the whole tree in a single repository or subject each package directory to its own repository. I'd prefer the later, since it allows a developer to pull only the relevant directories from source control (That's Option B)

The base directory, containing the artifacts that won't be packaged shall be named HappySoup. While it is a rather colloquial term, it is well established

I'm a little old fashioned when it comes to directory names: no spaces, double byte characters (that includes ?) or special characters

You need to pay attention to sfdx-project.json and .sfdx as well as .gitignore. More and that below

When you have mixed OS developer communities using Windows, MAC or Linux, directory delimiters could become a headache. My tongue-in-cheek recommendation for Windows would be to use WSL

Initially you want to divide, but not yet package. So your projects need to know about each other. Higher level packages, that in future will depend on base packages need to know about them and each package needs to know about the HappySoup. To get there I adjust my sfdx-project.json:

The key here are the relative path entries like ../HappySoup/force-app. When you use sfdx force:source:push the content gets pushed to your scratch org, so it is complete. When you use sfdx force:source:pull changes you made are copied down to the default path, so the adjacent projects remain as is.

When using pull and push from VSCode it will use the default user name configured for SFDX. To ensure that you don't push to or pull from the wrong place, you need to create one scratch org each using sfdx force:org:create --f config/project-scratch-def.json -a [ScratchOrgAlias] and then execute sfdx force:config:set defaultusername=[ScratchOrgAlias].

The command will create a .sfdx directory and config files inside in your project. Unless all developers checking out that repository use the same aliases (unlikely), you want to add .sfdx to your .gitignore file.

Now you are all set to move files from the happy soup to future package directories. With the relative path in your sfdx-project.json no packaging is required now and you still can get a fully functioning scratch org.

One pro tip: instead of relying on individual scratch definition files, you might opt to use the one in the happy soup, so all your scratches have the same shape.

When embarking on the SFDX package journey, the start is Phase 0. You have an org that contains all your meta data and zero or more (managed) packages from AppExchange. That's the swamp you want to drain.

Before you move to phase 1, you need to be clear how you want to structure your packages. High level could look like this:

You have an unpackaged base, that over time will shrink. The interesting challenge is to deal with dependencies there

Some of the components will be used across all system - most likely extensions to standard objects or triggers and utility classes. Core LWC components are good candidates for base packages too. There can be more than one base package

Your business components. Slice them by business function, country specifics or business unit. Most likely will resemble some of your organization structure

A package from AppExchange or a legacy package will not depend on anything. In my current project we moved all VisualForce stuff (pages and controllers) there, since these won't be needed after the lightning migration is concluded and then can be uninstalled easily.

A common setup in many organizations is to outsource development and/or operation to a system integrator. For agile organizations that can post a challenge. A key is skillfulness - how fast and good can it be implemented?

Competition is supposed to keep cost at bay, however customer relation and familiarity with the environment (In Dreamland everything is documented) pose a substantial barrier to entry. A barrier to entry will enable an incumbent vendor to charge more.

So an engagement manager might see him/herself confronted with an interesting dynamic.

There are a slow and a fast loop running concurrently. Depending on the planning horizon, the engagement manager might not see the outer loop to the detriment of all participants. Let me walk you through:

Investment in better tools or skills leads to improved efficiency. Work is delivered faster, closer to actual requirements and with less defects

In the short run this leads to a reduction in hours sold (bad for time and material contracts)

A reduction in hours sold leads to reduced profitability since you have more resources sitting on the bench

In conclusion: As long as the barrier to entry protects you, investing in efficiency is bad for the bottom line. So investment in efficiency should only be made to keep the barrier to entry high enough (Add you own sarcasm tag here). However there's a longer running loop in motion:

Improved efficiency leads to better quality and shorter delivery time. Work is done fast and good (which might justify higher charges per hour)

Getting good quality soon leads to an increase in customer satisfaction. Who doesn't like swift and sure delivery

Happy customers, especially when delivery times are short, will find an endless stream (only throttled by budget) of additional requirement to implement

Having more and more new requirements coming in, keeps people off the bench and keeps utilization high. High utilization is the base of service profitability

About Me

Contact

Disclaimer

This site is in no way affiliated, endorsed, sanctioned,
supported, nor enlightened by my current or previous employers.
I may be an employee, but the opinions, theories, facts, etc.
presented here are my own and are in now way given in any official
capacity. In short, these are my words and this is my site, not
my current or former employers' - and don't even begin to think otherwise.