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
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:
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:
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>
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 } })
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.
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
LikeLike
Example added 🙂
LikeLike
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.
LikeLike
Nice one
LikeLike