Lightning Component Action Service

Like a lot of Salesforce developers, I’m very excited by the Lightning Component Framework now in beta in Spring 15. A modern, component-oriented, UI framework is here just in time. Even better, it is a component framework built to spec for the Force.com platform and the multi tenant cloud architecture that makes Salesforce tick. The Action Service is the way your component talks to the server. So it is not trivial, and there are a few features that you might overlook initially that could be very helpful.

The Action Service is wrapped in a specific part of the Lightning Components Javascript API. Recently, Peter Knolle did an excellent blog post on the Salesforce Developers web site to introduce the basics of the Javascript API. If you’ve not learned anything about how Javascript works in Lightning Components, I’d suggest you start with that post.

The Action object is the way you get to the Action service. In this and a few upcoming articles I will dive deep into one particular part of that API. I’ve built a series of compoments to illustrate some of the features that can be found in this github repo. The first is a donut chart. For fun, I wired it up to a Lightning Connect external object. That’s not so important…but it did give me a plausible reason for building this component for Lightning App Builder when there already was a charting component available! Here’s what it looks like:

Donut Chart In Action

To review how Action works here are the basics:

The Server Action

First I need something that is the server-side representation of Action. In the world of Lightning Components, this is a static Apex method declared as @AuraEnabled.

DonutChartService.cls
1
2
3
4
5
6
7
public class DonutChartService{
@AuraEnabled //<--make method available to Lightning Components JS API
public static Map<String,Decimal> getOrdersOverNUnits(Decimal n){
...
return responseData; //this would be the populated map of data
}
}

For the sake of understanding the result, this method returns a map populated like this:

Key Description Example Value
total-item-count How many things did I count 1000
item-count How many met a certain criteria 457

I then use these two values to eventually produce the percentile result displayed by the component.

Connect Apex Class to the Component

The component markup then needs to be told about the Apex class that contains the @AuraEnabled action using the controller attribute in the aura:component root tag that declares the component.

SimpleDonutChart.cmp
1
2
3
4
5
6
7
8
9
<aura:component controller="DonutChartService">
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />

<aura:attribute name="total" type="Integer" default="360"/>
<aura:attribute name="chartValue" type="Integer" default="0"/>
<aura:attribute name="chartUnitsThreshold" type="Integer" default="20"/>

...the rest of the markup...
</aura:component>

Using the Action

Finally we’ll have something in our component that needs to access the server action. In the case of the donut chart, this happens whenever the component goes through init.

SimpleDonutChartController.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
doInit: function(component, event, helper) {

var action = component.get('c.getOrdersOverNUnits');

action.setParams({
n:component.get('v.chartUnitsThreshold')
});

action.setCallback(this,function(resp){
var respData = resp.getReturnValue();
component.set('v.total',respData['total-item-count']);
component.set('v.chartValue',respData['item-count']);
});

$A.enqueueAction(action);
}

On line 3, we create action object which is an instance of the Action feature found in the Lightning Components Javascript API. By using component.get we bind it to the server-side functionality defined in our Apex method previously.

After that we need to pass in the value for the n parameter for the getOrdersOverNUnits method. This is done on lines 5-7.

In each of these two instances there is a slight difference in thing we are looking for using component.get. In the first instance it is c. and the second it is v.. These are called value providers. In the case of c this references the server-side class we bound using the controller attribute in the component. The v provider references the attributes in a component.

Lines 9-13 are to assign the callback handler that will set attribute values in the component once we get the data back.

Finally, line 15 takes the entire server request/params/callback structure we’ve defined in this instance of Action and sticks it in Action Service queue, which will complete the server request.

If you’re a seasoned web or JS developer, you may be asking yourself, “but why all this extra stuff, instead of just firing up an XHR and calling the server?”

Multi Tenant, Multi Author, and Composite

The Salesforce multi tenant architecture is easily overlooked. It is so much a part of what we do, we can almost forget it is there…like the rails under a train, once you’re on the train, they’re invisible, you can forget they are there, but everything about your journey is about those pieces of iron stringing you from point to point.

So it is with the multi tenant architecture. A customer might get their environment, add some users, create and deploy a few apps for their business and never consider that behind the scenes they are sharing servers, infrastructure, network, database tables, CPU, and memory with other customers.

Consider also that there are always at least two authors in an environment. Salesforce has built a lot of features that are delivered as standard, but a customer is going to build their own features, their own configurations, code, and custom UI. Getting code from two developers, who are essentially anonymous to each other, to work together is critical to our platform.

Add a few AppExchange apps in and you suddenly could have three, four, or more authors who have created discrete components in this one environment, all on the same web page, each potentially attempting to access the server, the same environment, and consequently all competing for bandwidth.

Many XHR Requests

What would be handy is if the framework itself knew that a server trip was needed, and could take several requests at once, if they happen in a similar time frame.

The Lightning Freight Train

Obviously, I wouldn’t be writing about this problem if there weren’t an answer in Lightning Components.

The Action Service performs exactly what is required in such a composite environment.

Each component, passing its action into $A.enqueueAction is oblivous to what other components are doing. But upon receiving an action, the queue will take that action, and any others that may happen around the same time, and make a single trip to the server.

"498 Bayfield - Flickr - drewj1946" by Drew Jacksich from San Jose, CA, The Republic of California

This feature is knicknamed boxcarring. Like freight cars stacked up all headed to the same destination, Action Service takes as many requests as it can back to the server, and brings back their responses, parcels them back to each component, then each component invokes its proper callback.

Instead of a bunch of components, each making their own XHR back to Salesforce, we end up with one single XHR that services any component that happens to need something from the server at around the same time.

Boxcarred Actions

This is good for Salesforce as it reduces the number of requests made to our servers. It should also be good for the client app (especially bandwidth limited mobile applications), as Lightning Components optimizes the number of actual requests made to the server.

If you’re an experienced Visualforce developer, this might be sounding a lot like a Javascript remoting feature buffer=true. And you’d be absolutely right. Without going into too many details, there are some limitations of JS remoting mostly due to Visualforce being shackled to an iFrame. But this is worth its own article at a later time.

And unlike Visualforce, there are some more advanced features of the Action Service that allow us to gain more control over how and when actions are processed such as for large requests, and requests that you want to defer until later. But those, too, will have to wait for another post.

Hopefully this puts some context around why Lightning Components has its own transport service, and how it will help your components get the most out of the Force.com platform.