A “Provider” Pattern in Salesforce Lighting

by Aidan Harding - July 27, 2017

As a developer coming to Lightning, one of the immediate questions is:

How can I share code between my components?

Well, there’s a nice summary of two code-sharing options on the Salesforce Developers Blog: Modularizing Code in Lightning Components. But,neither of those quite address the use-case that I want to address:

Javascript libraries – this is pretty much opting out of the Lightning framework. Lighting is a component oriented framework with some Object Oriented characteristics, so I’d like to use them! I could use Javascript libraries to do anything that anyone else does with them (modules and so on), but it means having blobs of code in static resources and that doesn’t seem very Salesforce.

A service component – i.e. a custom Lightning component which doesn’t need a UI part, but exists just to wrap up some code. The code is exposed using <aura:method> and results are retrieved via a callback. That’s fine, but it requires the component using my shared code to have a Javascript controller. I want to separate concerns and keep Javascript out of components that don’t need it.

A “Provider” Component

My solution is to use something that I’ve termed a Provider Component. Naming-wise, I’d be interested to know if others are using the same thing under a different name. Or even if I have clashed names with someone calling something else a Provider. The idea is a little similar to the service component, above, but it provides data via variable binding instead of by method calls.

A provider fetches a record or collection of records via Apex. It shares its data via variable binding. It may do some processing on that data before sharing it with whoever is consuming the data. The properties that I want from a Provider Component are:

Sharing by composition – in Object Oriented design, we tend to favour composition over inheritance. There are multiple reasons for this, but the important one here is that when I bring shared code into my component by composition, it doesn’t stop me from doing more composition to bring in more shared code. If I use inheritance, I only get to choose one component to extend from.

Efficiency in server calls – if multiple components on the screen need the same information, then it should result in a single server call.

Simplicity of use – if I use the shared code inside a component with no Javascript controller, I don’t want to add a controller, just to get access to the shared code

No central coordinator – one way to achieve the sharing of information retrieved via Apex would be to have a container component fetch the records and pass them down to the children. This means the container needs to know too much about the internal requirements of its children, and that the child components cannot be easily re-used in other contexts.

A Provider In Action

Let’s take a concrete example. We have an very simple app where we will display articles of various types. Each article type has its own styling that should be driven by Custom Metadata so that the types can be expanded/modified later without changing the components.

The Article component displays a card-like item for an Article. It has no controller, but it does have an instance of the CategoryMetadataProvider. We use two attributes on the provider: categoryName is an input parameter (Lightning doesn’t make the distinction, but it’s useful to think of it that way) where we pass in the category name that we require information about. categoryMetadata is an output parameter (again, only output in the way we think about it) where we will receive the metadata about the given category. By the magic of attribute binding, the categoryMetadata attribute in Article will be updated when theCategoryMetadataProvider updates its categoryMetadata attribute (note that the names don’t have to match, it just tends to make sense to use the same name).

In the doInit function, we use the Apex controller to load the custom metadata (ensuring to set that as a storeable action, so that Lightning will cache the results and avoid calling the server multiple times for the same data). We then store the results in an object, so that we can access them like a Map using DeveloperName as a key.

Since the updateCategoryMetadata function is bound to changes on both categoryMetadataByName and categoryName, we don’t need to call it after doInit. It gets called automatically when categoryMetadataByName is set. All that updateCategoryMetadata does is check that we have the categoryMetadataByName object and categoryName initialised, then set the correct value for categoryMetadata.

It doesn’t matter which value comes in first: categoryMetadataByName or categoryName. The right value is settled for categoryName in the end.

By using a Provider pattern, we have wrapped up the logic of accessing an Apex controller to retrieve Category Metadata records. Not only does this mean that we have reusable code, we have also separated concerns and gained run-time efficiency.

If we had to write an ArticleHeader component to go with our Article component, then ArticleHeader would also need to know about Category Metadata. It could get that information by just including an instance of CategoryMetadataProvider. No need to write more Javascript. And, if Article and ArticleHeader are used on the same page, then the fact that we are use storeable actions means that Lightning will cache the values and, thus, only make the server call once.

Isn’t this just Lightning Data Service?

Well, yes, Lightning Data Service is an instance of this pattern (and, in some ways, more). But Data Service is not GA. Safe Harbour, and all that: write your own Provider until Salesforce make Data Service GA. And when Data Service is released, but only supports single records? Write your own Provider. Have more complex requirements that Data Service doesn’t support? Write your own Provider.

The fact that Salesforce are doing much the same thing as me here makes me think that it’s either a good idea, or obvious. But, most good ideas are obvious in retrospect. And, if I’m being obvious, then that’s fine!