Dynamic User Interfaces with Lightning Aura Components : Synchronizing the DOM with the Application State

Ángel Alberto Mata
7 min readApr 20, 2019

As Salesforce developers, we have to extend the implementation of the company we work to provide a customized experience for our co-workers in other areas. As time passes, I notice that Salesforce interfaces, especially in its classic version, are just a collection of web forms to insert records in the database. However, the requirements we face for the front end usually describe applications with highly interactive behavior, and the solutions should be implemented using the Salesforce toolset; how do we do that?

The ancient Visualforce does well its job with static web forms, but just like any other page-centered framework, it was not primarily designed to support dynamic contexts. Although there are some workarounds for this, everything keeps running on the server side, increasing the application response time. To manage Visualforce as a client-side application, we need to manipulate the raw DOM after a page returns to the browser, turning our apex page into a JavaScript application.

The Aura framework, on the other hand, is already integrated with JavaScript, allowing developers to run applications on the browser without tricky hacks. It imposes a composition architecture and provides data bindings to make cleaner the data flow across components. The programming model is pretty different compared with Visualforce, but it also represents a considerable improvement as a web framework.

In this article, I’ll try to build the context to develop interactive interfaces with Aura. While it’s not intended to introduce beginners to the framework’s syntax, it gives some hints to compare standard JavaScript with modern web frameworks. The syntax topics are covered by the official documentation and the well-known Trailhead courses.

The basic problem

With vanilla HTML and JavaScript coding, developers have to implement their own sequences to turn data into a particular look of the interface. If this data change either by user input or other kinds of events, the interface should change accordingly. Managing this task is somewhat easy for small pages, but as web applications become more complex, implementing that logic using linear sequences for every event handler is hard to follow and maintain in the long term.

Let’s look at the simplest case, the task of projecting user input back to the screen. With the help of jQuery, the code looks something like this:

<!-- string_projection.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="jquery-3.3.1.min.js"></script>
<script src="string_projection.js"></script>
</head>
<body>
<input id="input_field" type="text" />
<div id="output_field"> </div>
</body>
</html>// string_projection.js
$(function () {
//DOM ready
var str = "";
var input_field = $('#input_field');
var output_field = $('#output_field');
input_field.on ( 'input', project_input_to_output ); function project_input_to_output () {
str = input_field.val();
output_field.val(str);
}
});
Output changes as fast as input comes in.

In this simple case, the str variable is unnecessary to link both tags. However, when several DOM sections react to other sources, either to server communication or new input events, variables like str serve us as a bridge between those events and the user interface. In this context, these variables represent the application state, and this structure is central for managing the logic of the view. It is not trivial that from the very beginning of .js frameworks, the main goal of these solutions has been to develop a layer of abstraction to synchronize this state with the DOM, making the application’s behavior more organic.

Now, to extend our previous example, we could program a sequence for each event that modifies the state and to wrap some redundancies in shared functions, keeping a linear path for every event that reaches the DOM. For complex projects, however, the data flow can be difficult to follow and maintain, since the number of sequences could get overwhelming. Another approach, of course, is to code our own implementation of an observer pattern to notify fields when the state has changed, and that’s not a trivial solution these days.

We do have better alternatives. JavaScript has evolved over the years encapsulating generic solutions using higher levels of abstraction. jQuery was a big improvement in terms of readability and portability, but it was just a wrapper library; like a series of macros for JavaScript. Modern web frameworks offer features taken from desktop platforms to run dynamic applications on the browser. They represent better architectures and developers can exploit them to build robust systems. Following this path and trying to replace Visualforce, Salesforce gave customers a solution that could compete with these new frameworks: the Lightning Aura.

The Salesforce’s .js Solution

The first thing we note from this framework is its composition architecture. Applications should be divided into significant parts and they must do what they were designed for and nothing more. This simplifies the general structure of the project and allows us to reuse components.

Let’s replicate our previous JavaScript application on this framework to illustrate some points:

<aura:application>    <!-- Component state -->
<aura:attribute name="str" type="String" />
<!-- Markup -->
<lightning:input value="{!v.str}" />
<div class="slds-m-around_small">
<span>{!v.str}</span>
</div>
</aura:application>
As fast as data the value of str is changed, the framework automatically updates the DOM.

The most remarkable thing of this textbook example is the lack of JavaScript code to project data back to the output. That’s possible thanks to expressions like {!v.[ATTRIBUTE_NAME]}. They are expanded in every place where they appear and their changes are synchronized with the DOM. In general terminology, these expressions are called data bindings and represent an essential tool of every modern web framework. Inside Aura, they are a communication channel in the parent-child component hierarchy.

Next, we have the <lightning:input/> tag. This section is not a simple input field but a custom component that encapsulates the HTML markup, its style definition and the routines needed to update str. The value of this attribute is shared with the field as a reference, which means that other child components can manipulate it and still maintain the same state across all of them. That way, the data flow in the hierarchy of components is bidirectional, and should be intuitive how to develop web forms using this programming model.

Some developers point out that this kind of data flow is hard to debug, but I found that works perfectly in some cases. The key idea is to track how far and attribute will affect the component tree. While some attributes are local to one component, others affect larger branches. However, only the root of the tree or one of its subsections should have the authority to update the rest of the hierarchy. We always should be able to track state changes to avoid unexpected behavior or another sort of errors. Anyway, expressions like {#v.[ATTRIBUTE_NAME]} (with the hash instead of the exclamation mark), allow us to enforce a unidirectional data flow, which seems to be less error prone but force us to use JavaScript code with simple cases.

There is a question remaining: how do aura components like aura:if or aura:iteration know when its local state has been modified? The key is a method handler named change. This handler detects transformations in the value of a single attribute and triggers a function to manage them. Doing so, we move code routines from reacting to application events to managing changes of the whole state. DOM events are just handled to update this structure and the rest is responsibility of a centralized sequence that updates the entire component; just like a simplified version of an observer pattern.

The List Example

Most frameworks show an elemental example of a list of items to introduce beginners into the basic syntax. The following git repo contains that basic example with a hard-coded list of leads. While it could be simpler, it tries to show the most common scenarios for an Aura application. It contains an example of custom events, data bindings, and conditional styling. Also, the directory is a Salesforce DX project, which simplifies the task of migrating the components between orgs.

A simple list of leads ordered by priority (Icons courtesy of Freepik from Flaticon).

Other perspectives and the future

When data bindings are not enough for the project, we could implement our own state handler as a JavaScript library. Another approach is the use of Redux, a library that promotes a one-way data flow. Although it was primarily designed for React.js, it can be also used with Aura.

With the launch of the Lightning Web Components this year, Salesforce pretends to embrace the new HTML and JavaScript standards. As developers, we have to take the decision of migrating or not Aura applications into these new components. However, what’s the point of using a lower-level and probably more complex syntax for something that Aura does in a decent way?

Of course, the framework has its own issues in terms of efficiency and consistency, but do the general solutions of these problems matter the cost of adapting our developments to HTML templates? I hope this model allow us to choose our own set of JavaScript tools for the front end because that would be a giant step in order to open Salesforce for every developer.

References

--

--

Ángel Alberto Mata

Salesforce Developer. Interested in open source projects and the integration of new tools that accelerate the development of Salesforce applications.