Bound and unbound expressions in Lightning Components

Today I want to take a look at the data binding possibilities that exist for Lightning Components attributes. We will see how attributes can be referenced using bound or unbound expressions, and which implications will these bindings have in our application.

The best way to explain this will be creating a demo component in which we will try to examine what is happening when using the different bindings. For this purpose, I have created the next components:

  • UnboundExpression.cmp is the parent component in which we have defined a String attribute called attr1.

UnboundExpression.cmp

<aura:component >
    <aura:attribute name="attr1" type="String" default="I love Lightning"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div class="slds-box">
        <p>PARENT COMPONENT</p>
        <lightning:formattedText value="{!v.attr1}"/>
        <lightning:button class="slds-m-left_medium" variant="brand" label="Submit" onclick="{!c.handleButtonClick}"/>
    </div>
    <div class="slds-box">
        <p>CHILD COMPONENT</p>
        <c:UnboundExpressionsSubcomponent attr2="{!v.attr1}"/>    
    </div>
</aura:component>

UnboundExpression.js


({
    doInit : function(component, event, helper) {
    component.set("v.attr1", "Attribute initialized in parent component!");
    },
    
    handleButtonClick : function(component, event, helper) {
    var myattr1 = component.get("v.attr1");
        component.set("v.attr1", "Button clicked in parent component!");
    }
})
  • UnboundExpressionSubcomponent.cmp is a child component that the parent component will instantiate and pass in its attribute attr1 into a second attribute attr2.

UnboundExpressionSubcomponent.cmp

<aura:component >
    <aura:attribute name="attr2" type="String" default="I love Lightning"/>
    
    <lightning:formattedText value="{!v.attr2}"/>
    <lightning:button class="slds-m-left_medium" variant="brand" label="Submit" onclick="{!c.handleButtonClick}"/>
    
</aura:component>

UnboundExpressionSubcomponent.js

({    
    handleButtonClick : function(component, event, helper) {
    var myattr2 = component.get("v.attr2");
        component.set("v.attr2", "Button clicked in subcomponent!");
    }
})

1) First, we will use {!v.attr1} and {!v.attr2}, which are bound expressions, for all the cases. This means that:

  • We are showing attr1 in a lightning:formattedText in the parent component using a bound expression
  • We are passing attr1 to attr2 from parent to subcomponent using a bound expression
  • We are showing attr2 in a lightning:formattedText inside the subcomponent using a bound expression

1bound-2bound

As you can see, these are the events that occur in the demo:

  • First, the doInit method of the parent component initializes attr1, on component load.
  • When button on parent component is clicked, attr1 and attr2 change.
  • When button on child component is clicked, attr1 and attr2 change.

Why can I see the formatted text changing in both components?

  • lightning:formattedText in parent component is aware of changes performed by both components because we are using a bound expression.
  • lightning:formattedText in subcomponent is aware of changes performed by both components because we:
    • passed the value using a bound expression
    • and we are using a bound expression for attr2

2) Let’s see what happens if we change the way in which attr1 is bound to lightning:formattedText in the parent component. This time we will use {!#v.attr1}, with is an unbound expression. Let’s modify the parent component markup to be the next one:

UnboundExpression.cmp

<aura:component >
    <aura:attribute name="attr1" type="String" default="I love Lightning"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div class="slds-box">
        <p>PARENT COMPONENT</p>
        <lightning:formattedText value="{#v.attr1}"/>
        <lightning:button class="slds-m-left_medium" variant="brand" label="Submit" onclick="{!c.handleButtonClick}"/>
    </div>
    <div class="slds-box">
        <p>CHILD COMPONENT</p>
        <c:UnboundExpressionsSubcomponent attr2="{!v.attr1}"/>    
    </div>
</aura:component>

In this case, this is what will happen:

case2

Why can I see the formatted text changing only in the subcomponent?

  • lightning:formattedText in parent component is not aware of changes performed by any of the components because we are using an unbound expression, so even the changes performed by its own init handler won’t be reflected.
  • lightning:formattedText in subcomponent is aware of changes performed by both components because we:
    • passed the value using a bound expression
    • and we are using a bound expression for attr2

3) Let’s change now the way in which attr1 is passed to attr2, from parent component to subcomponent, and do it using an unbound expression this time:

UnboundExpression.cmp

<aura:component >
    <aura:attribute name="attr1" type="String" default="I love Lightning"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div class="slds-box">
        <p>PARENT COMPONENT</p>
        <lightning:formattedText value="{#v.attr1}"/>
        <lightning:button class="slds-m-left_medium" variant="brand" label="Submit" onclick="{!c.handleButtonClick}"/>
    </div>
    <div class="slds-box">
        <p>CHILD COMPONENT</p>
        <c:UnboundExpressionsSubcomponent attr2="{#v.attr1}"/>    
    </div>
</aura:component>

This time, these are the results I get:

case3.gif

What is happening now?

  • lightning:formattedText in parent component is not aware of changes performed by any of the components because we are using an unbound expression, same case as before.
  • lightning:formattedText in subcomponent is not aware of changes performed by parent component because we passed the value using an unbound expression.
  • lightning:formattedText in subcomponent is aware of changes performed by subcomponent,  because we are using a bound expression for attr2.

4) Last, I want to show you what happens if we use unbound expressions in any case:

UnboundExpressionSubcomponent.cmp

<aura:component >
    <aura:attribute name="attr2" type="String" default="I love Lightning"/>
    
    <lightning:formattedText value="{#v.attr2}"/>
    <lightning:button class="slds-m-left_medium" variant="brand" label="Submit" onclick="{!c.handleButtonClick}"/>
    
</aura:component>

case4.gif

This time:

  • lightning:formattedText in parent component is not aware of changes performed by any of the components because we are using an unbound expression.
  • lightning:formattedText in subcomponent is not aware of changes performed by parent component because we passed the value using an unbound expression.
  • lightning:formattedText in subcomponent is not aware of changes performed by subcomponent,  because we are using an unbound expression for attr2.

To investigate a bit more, I have taken a look at the app.js autogenerated file, looking for differences in the case of using bound expressions and when using unbound ones. What have found is that there is a component service in which components structure is registered, and depending on the way you bind the attributes, it will have a property called “byValue”a set to true (unbound) or false (bound).

$A.componentService.addComponent("markup://aazcona:UnboundExpressions", (function (){
    ...
    "componentDef":{
        "descriptor":"markup://lightning:formattedText",
        "type":"module"
    },
    "attributes":{
        "values":{
            "value":{
                "descriptor":"value",
                "value":{
                    "exprType":"PROPERTY",
                    "byValue":true,
                    "target":"aazcona:UnboundExpressions",
                    "path":"v.attr1"
                }
            }
        }
    }
    ...
}

If you think about it, it makes sense to identify unbound expressions as a way to pass parameters by value, and bound expressions as the way to pass them by reference, as attributes in both components will remain bound.

One important thing to remember if you use unbound expressions is that change events won’t be propagated to the subcomponents you are passing the attributes in. So, defining a change handler, which will perfectly work for bound expressions, won’t work for unbound ones.

An example of how to define a change handler would be:

UnboundExpressionSubcomponent.cmp

<aura:component >
    <aura:attribute name="attr2" type="String" default="I love Lightning"/>
    <aura:handler name="change" value="{!v.attr2}" action="{!c.handleValueChange}"/>
    ...
    
</aura:component>

UnboundExpressionSubcomponent.js

({
    handleValueChange : function (component, event, helper) {
        // handle value change
    }
})

Salesforce recommendation is that you should be using unbound expressions when possible, as bi-directional data binding is expensive for performance and it can create hard-to-debug errors.

As a conclusion, you should try to use unbound expressions whenever possible to pass in attriibutes, except if:

  • …you want to set a value on the attribute using the parent javascript controller
  • …you want to implement a change handler for the attribute on the subcomponent

Here you have the code I have used for the demo.

3 thoughts on “Bound and unbound expressions in Lightning Components

  1. Hi Alba
    thanks for your detailed explanation
    how ever please can you give one scenario
    on you want to implement a change handler for the attribute on the subcomponent

    Like

  2. I think a good way to choose is:
    Use unbounded expressions to render static data that won’t change until you need to refresh a page – table headers would be a good example.
    Use bound expression if it needs to be dynamic – pagination inside the table changes the cell data.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s