Explaining Expressions in the Aura Framework: Part 1

At a basic level, expressions in the Aura Framework let us reference values in our markup file. However, expressions are a lot more complex than that. With this article, I hope we can unlock the full potential of expressions for you as a Lightning Developer. You’ll learn all about the two different types of expressions, their different capabilities and how to consider performance when using expressions.

Let’s start at a high level overview of the different expressions and their flavors, then dig into each to give a thorough description.

What is an expression?

In Aura, an expression is a reference or a calculated value, typically created in your markup file using the bracket bang syntax as shown below.

Here are some examples from different frameworks you might be familiar with.

<!-- Aura -->
<b>{!v.title}</b>

<!-- VisualForce -->
<b>{!title}</b>

<!-- React and Lightning Web Components -->
<b>{title}</b>

Note: {! v.expression } was indeed chosen for Aura because of the patterns in VisualForce. It made sense at the time to use the syntax common to our core platform developers.

Different types of expressions

In Aura, there are two different types of expressions each with two flavors of their own.

Expression types:

  • Property Reference expressions
  • Calculated expressions

Expression flavors:

  • By Reference
  • By Value

Here are some examples with the different types of expressions:

// Property Reference By Reference: Live two way reference to v.attribute
{! v.attribute }

// Property Reference By Value: Raw value of v.attribute, any changes will not notify this reference.
{# v.attribute }

// Calculated Expression that calculates a value
{! v.myInteger * 3.14 }

// Calculated Expression using an expression function
{! join(',', v.attr1, v.attr2, v.attr3) }

// Calculated Expression By Value: Calculate the value but don't 
// try to calculate it again in the future.
{# 2+2 }

You can see that in addition to curly-bang ({!}) we also have curly-pound syntax ({#}) for by value referencing. We’ll get into that more here soon.

Where you can use expressions

When using expressions for attributes, it’s important to remember that your markup file must be valid XML. This means you must always include quotation marks around your attribute values.

// Good
// Quoted and end tag.
<div class="{!v.className}"></div>

// Bad, no end tag, no quotes around the expression. 
// Results in Compile Error.
<div class={!v.className}>

Note: For Aura you need to include the quotations around your references. For Lightning Web Components you do not include the quotes.

Using expressions plainly in your markup will output what is in them if the output is either text, numbers, components or DOM elements.

<h1>{!v.title}</h1>

You can pass them as values to attributes to other components.

<ui:button label="{!v.title}"/>

You can also use them as default values for attributes.

<aura:attribute type="Integer" name="int20" default="{!v.int+20}"/>

Just be aware that the order the attributes are defined in is the order we will process them in. Do not reference an attribute that is not declared above the attribute where you reference it.

Let’s now dig in to each of the references and see how they differ and when would you choose one over the other.

Property reference expressions

By Reference

The most common expression to use is the curly-bang {!..} syntax. When its value changes it will notify all other referencing expressions and components.

This, of course, has a few costs.

  • Any component referencing that expression will need to be re-rendered.
  • Expressions can be chained together, so any expression referencing this expression would need their components to be re-rendered.
  • The reference is a two-way data binding. Updates to the reference will propagate through the chain in both directions.
  • All the change handlers for the expression and referencing expressions will need to be fired.

To visualize two way data binding, let us look at an example including two components: a button and a label.

// button.cmp
<aura:component>
    <aura:attribute name="buttonText" type="String" default="Button"/>
    <c:label labelText="{!v.buttonText}"/> <button>{!v.buttonText}</button>
</aura:component>

// label.cmp
<aura:component >
    <aura:attribute type="String" name="labelText"/>
    <aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
    {!v.labelText} 
</aura:component>

// labelController.js
onInit: function(cmp) {
    cmp.set("v.labelText", cmp.get("v.labelText") + " Label")
}

// Result
console.log(buttonCmp.get("v.buttonText")); // "Button Label";
console.log(labelCmp.get("v.labelText")); // "Button Label"

Result:

Since we used two way binding to pass the value to the label, changes to the reference in either component notify the others of their value being updated.

This causes the button to also have “Button Label” in it, which you would not want. You’ll probably just want it to say “Button” in this case. So let’s talk about By Value to see what we can do about that!

By Value

By Value is denoted by using a curly-pound syntax. {#…}

When using By Value expressions, the framework can optimize by not creating any data bindings. This can have a large performance benefit when done extensively across a code base. The main downside is you lose the automatic reactive data updates you would normally expect.

In the previous example we saw that a label set one of its properties and it notified the source attribute of the change. By using By Value expressions, we can prevent this.

// button.cmp
<aura:component>
    <aura:attribute name="buttonText" type="String" default="Button"/>
    <c:label labelText="{#v.buttonText}"/> <button>{!v.buttonText}</button>
</aura:component>

// label.cmp
<aura:component >
    <aura:attribute type="String" name="labelText"/>
    <aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
    {!v.labelText} 
</aura:component>

// labelController.js
onInit: function(cmp) {
    cmp.set("v.labelText", cmp.get("v.labelText") + " Label")
}

// Result
console.log(buttonCmp.get("v.buttonText")); // "Button";
console.log(labelCmp.get("v.labelText")); // "Button Label"

In this case, we’re passing v.labelText by value. Since there is no reference between the two attributes, changes to the label do not notify the button.

This is NOT one way data binding. This is a one time binding. What we do is get the value of the reference to v.buttonText, then we pass the raw value to the label. Any changes to the buttonText in the future are ignored.

The challenge here is that changes to the attribute in the button do not notify the label either. You’ll have to do it manually such as adding a changeHandler for detecting v.buttonText changes and manually set the new value on the label.

This is starting to be a lot of code for a simple pattern. What else could we do?

In this situation it’s possible to use a Calculated Expression, so let’s talk about what that would look like next.

Calculated expressions

When you do anything more than reference a value in an expression, it turns into a computed expression. This computed expression is something the framework passes around and executes either when the expression value is needed or when one of the attributes it references changes.

By Reference or By Value?

Just like Property Reference Values, you can use By Value or By Reference when creating function call values. Just like Property Reference Values, when using By Value, we will evaluate the expression and only use the calculated value in the future.

When using By Value though for Function Call Values, you’re losing most of the power of the Function Call Value. The only half-sensible use case for a By Value Function Call Value is operating on static values and even then, it doesn’t make sense for the overhead.

So at this time, the suggestion is to always use By Reference.

Creating Function Call Values

Function Call Values are automatically created by computing any values. We can use that to simplify our markup.

// button.cmp
<aura:component>
    <aura:attribute name="buttonText" type="String" default="Button"/>
    <c:label labelText="{!v.buttonText}"/> <button>{!v.buttonText}</button>
</aura:component>

// label.cmp
<aura:component >
    <aura:attribute type="String" name="labelText"/>
    {!v.labelText + ' Label'} 
</aura:component>

// Result
console.log(buttonCmp.get("v.buttonText")); // "Button";
console.log(labelCmp.get("v.labelText")); // "Button";

Result:

While this would not update the value of v.labelText, at least it visually shows the passed in labelText with the additional value ‘Label’ added.

Conclusion

Hopefully this brief overview gives you a great foundation to use the different types of expressions in your components. We’ll be following this article with another that details all the different things you can do with these two expression types.

To give a brief overview of the important points of this post:

When to use By Value
When you’re not expecting updates to the expression, you can get performance benefits by using By Value. However, it’s best to avoid them when using a Function Call value.

What a Calculated expression is
Calculated expressions are when you calculate a value instead of directly referencing a property. Use them to avoid having to pre-process values in your JavaScript helpers and controllers.

Expressions are two way data binding
Limiting expressions is best for performance because of this.

To learn more, you’ll want to take the Attributes and Expressions topic in the Lightning Components Basics Trailhead module.

I look forward to part 2 and if you have any questions, I’d love to hear them on Twitter at @GrayJustise.