This concept is a little tricky, but it will make more sense when we look at an example. Consider a c:parent component that has a parentAttr attribute. c:parent contains a c:child component with a childAttr attribute that’s initialized to the value of the parentAttr attribute. We’re passing the parentAttr attribute value from c:parent into the c:child component, which results in a data binding, also known as a value binding, between the two components.
<!--c:parent--> <aura:component> <aura:attribute name="parentAttr" type="String" default="parent attribute"/> <!-- Instantiate the child component --> <c:child childAttr="{!v.parentAttr}" /> </aura:component>
{!v.parentAttr} is a bound expression. Any change to the value of the childAttr attribute in c:child also affects the parentAttr attribute in c:parent and vice versa.
Now, let's change the markup from:
<c:child childAttr="{!v.parentAttr}" />
to:
<c:child childAttr="{#v.parentAttr}" />
{#v.parentAttr} is an unbound expression. Any change to the value of the childAttr attribute in c:child doesn’t affect the parentAttr attribute in c:parent and vice versa.
Here’s a summary of the differences between the forms of expression syntax.
Let’s look at another example of a c:parentExpr component that contains another component, c:childExpr.
Here is the markup for c:childExpr.
<!--c:childExpr--> <aura:component> <aura:attribute name="childAttr" type="String" /> <p>childExpr childAttr: {!v.childAttr}</p> <p><lightning:button label="Update childAttr" onclick="{!c.updateChildAttr}"/></p> </aura:component>
Here is the markup for c:parentExpr.
<!--c:parentExpr--> <aura:component> <aura:attribute name="parentAttr" type="String" default="parent attribute"/> <!-- Instantiate the child component --> <c:childExpr childAttr="{#v.parentAttr}" /> <p>parentExpr parentAttr: {!v.parentAttr}</p> <p><lightning:button label="Update parentAttr" onclick="{!c.updateParentAttr}"/></p> </aura:component>
The c:parentExpr component uses an unbound expression to set an attribute in the c:childExpr component.
<c:childExpr childAttr="{#v.parentAttr}" />
When we instantiate childExpr, we set the childAttr attribute to the value of the parentAttr attribute in c:parentExpr. Since the {#v.parentAttr} syntax is used, the v.parentAttr expression is not bound to the value of the childAttr attribute.
The c:exprApp application is a wrapper around c:parentExpr.
<!--c:exprApp--> <aura:application > <c:parentExpr /> </aura:application>
In the Developer Console, click Preview in the sidebar for c:exprApp to view the app in your browser.
Both parentAttr and childAttr are set to “parent attribute”, which is the default value of parentAttr.
Now, let’s create a client-side controller for c:childExpr so that we can dynamically update the component. Here is the source for childExprController.js.
/* childExprController.js */ ({ updateChildAttr: function(cmp) { cmp.set("v.childAttr", "updated child attribute"); } })
Press the Update childAttr button. This updates childAttr to “updated child attribute”. The value of parentAttr is unchanged since we used an unbound expression.
<c:childExpr childAttr="{#v.parentAttr}" />
Let’s add a client-side controller for c:parentExpr. Here is the source for parentExprController.js.
/* parentExprController.js */ ({ updateParentAttr: function(cmp) { cmp.set("v.parentAttr", "updated parent attribute"); } })
In the Developer Console, click Update Preview for c:exprApp.
Press the Update parentAttr button. This time, parentAttr is set to “updated parent attribute” while childAttr is unchanged due to the unbound expression.
Now, let’s update the code to use a bound expression instead. Change this line in c:parentExpr:
<c:childExpr childAttr="{#v.parentAttr}" />
to:
<c:childExpr childAttr="{!v.parentAttr}" />
In the Developer Console, click Update Preview for c:exprApp.
Press the Update childAttr button. This updates both childAttr and parentAttr to “updated child attribute” even though we only set v.childAttr in the client-side controller of childExpr. Both attributes were updated since we used a bound expression to set the childAttr attribute.
You can configure a component to automatically invoke a change handler, which is a client-side controller action, when a value in one of the component's attributes changes.
When you use a bound expression, a change in the attribute in the parent or child component triggers the change handler in both components. When you use an unbound expression, the change is not propagated between components so the change handler is only triggered in the component that contains the changed attribute.
Let’s add change handlers to our earlier example to see how they are affected by bound versus unbound expressions.
Here is the updated markup for c:childExpr.
<!--c:childExpr--> <aura:component> <aura:attribute name="childAttr" type="String" /> <aura:handler name="change" value="{!v.childAttr}" action="{!c.onChildAttrChange}"/> <p>childExpr childAttr: {!v.childAttr}</p> <p><lightning:button label="Update childAttr" onclick="{!c.updateChildAttr}"/></p> </aura:component>
Notice the <aura:handler> tag with name="change", which signifies a change handler. value="{!v.childAttr}" tells the change handler to track the childAttr attribute. When childAttr changes, the onChildAttrChange client-side controller action is invoked.
Here is the client-side controller for c:childExpr.
/* childExprController.js */ ({ updateChildAttr: function(cmp) { cmp.set("v.childAttr", "updated child attribute"); }, onChildAttrChange: function(cmp, evt) { console.log("childAttr has changed"); console.log("old value: " + evt.getParam("oldValue")); console.log("current value: " + evt.getParam("value")); } })
Here is the updated markup for c:parentExpr with a change handler.
<!--c:parentExpr--> <aura:component> <aura:attribute name="parentAttr" type="String" default="parent attribute"/> <aura:handler name="change" value="{!v.parentAttr}" action="{!c.onParentAttrChange}"/> <!-- Instantiate the child component --> <c:childExpr childAttr="{!v.parentAttr}" /> <p>parentExpr parentAttr: {!v.parentAttr}</p> <p><lightning:button label="Update parentAttr" onclick="{!c.updateParentAttr}"/></p> </aura:component>
Here is the client-side controller for c:parentExpr.
/* parentExprController.js */ ({ updateParentAttr: function(cmp) { cmp.set("v.parentAttr", "updated parent attribute"); }, onParentAttrChange: function(cmp, evt) { console.log("parentAttr has changed"); console.log("old value: " + evt.getParam("oldValue"));ui console.log("current value: " + evt.getParam("value")); } })
In the Developer Console, click Update Preview for c:exprApp.
Open your browser’s console (
in Chrome).Press the Update parentAttr button. The change handlers for c:parentExpr and c:childExpr are both triggered as we’re using a bound expression.
<c:childExpr childAttr="{!v.parentAttr}" />
Change c:parentExpr to use an unbound expression instead.
<c:childExpr childAttr="{#v.parentAttr}" />
In the Developer Console, click Update Preview for c:exprApp.
Press the Update childAttr button. This time, only the change handler for c:childExpr is triggered as we’re using an unbound expression.