I'm seeing some unexpected behaviour when passing an SObject attribute through multiple descendant components, in that when I update a field on the SObject the change event is fired for each descendant component, but not the ancestors.
I've dwindled this down to the following component nesting:
Level 1 -> Level 2 -> Level 3
I have two attributes - one an account SObject and one a regular String. I set the default value for these in my Level 1 component, and pass them as attributes to Level 2 and thence to Level 3.
Here are the three components and their controllers - they are pretty simple in terms of what they do and all provide much the same functionality:
Level 1 Component:
<aura:component implements="force:appHostable">
<aura:attribute name="acc" type="Account"
default="{ 'sobjectType': 'Account',
'Name': 'Name'
}" />
<aura:handler name="change" value="{!v.acc.Name}" action="{!c.accChanged}"/>
<aura:handler name="change" value="{!v.str}" action="{!c.strChanged}"/>
<aura:attribute name="str" type="String" default="Str"/>
<aura:handler name="change" value="{!v.items}" action="{!c.itemsChange}"/>
<div>Level 1 name : {!v.acc.Name}</div>
<div>Level 1 Str : {!v.str}</div>
<div><ui:button press="{!c.changeName}" label="Press to Change"/></div>
<bblightning:Level2 acc="{!v.acc}" str="{!v.str}" />
</aura:component>
Level 1 Controller:
({
changeName : function(component, event, helper) {
component.set('v.acc.Name', 'Name from 1!');
component.set('v.str', 'Str from 1!');
},
accChanged : function (){
console.log('Level 1 : Account changed');
},
strChanged : function (){
console.log('Level 1 : String changed');
}
})
Level 2 Component:
<aura:component >
<aura:attribute name="acc" type="Account"/>
<aura:attribute name="str" type="String"/>
<aura:handler name="change" value="{!v.acc.Name}" action="{!c.accChanged}"/>
<aura:handler name="change" value="{!v.str}" action="{!c.strChanged}"/>
<div>Level 2 name : {!v.acc.Name}</div>
<div>Level 2 Str : {!v.str}</div>
<div><ui:button press="{!c.changeName}" label="Press to Change"/></div>
<bblightning:Level3 acc="{!v.acc}" str="{!v.str}" />
</aura:component>
Level 2 Controller:
({
changeName : function(component, event, helper) {
component.set('v.acc.Name', 'Name from 2!');
component.set('v.str', 'Str from 2!');
},
accChanged : function (){
console.log('Level 2 : Account changed');
},
strChanged : function (){
console.log('Level 2 : String changed');
}
})
Level 3 Component:
<aura:component >
<aura:attribute name="acc" type="Account"/>
<aura:attribute name="str" type="String"/>
<aura:handler name="change" value="{!v.acc.Name}" action="{!c.accChanged}"/>
<aura:handler name="change" value="{!v.str}" action="{!c.strChanged}"/>
<div>Level 3 name : {!v.acc.Name}</div>
<div>Level 3 Str : {!v.str}</div>
<div><ui:button press="{!c.changeName}" label="Press to Change"/></div>
</aura:component>
Level 3 Controller :
({
changeName : function(component, event, helper) {
component.set('v.acc.Name', 'Name from 3!');
component.set('v.str', 'Str from 3!');
},
accChanged : function (){
console.log('Level 3 : Account changed');
},
strChanged : function (){
console.log('Level 3 : String changed');
}
})
Accessing Level 1 shows the following output, which the default attributes are being passed correctly through at setup:
Pressing the level 1 button correctly updates the attributes and cascades down through the descendants:
and the console.log output shows the change events firing as expected:
However, reloading the page and pressing the Level 3 button only updates the String attribute in the ancestors, not the Account:
and the console.log output confirms that the change event for the Account object is not being received by the ancestors:
My initial thought was that the issue here was that I'm changing a field on a complex object which doesn't fire the change event as that is tied to the object reference itself, but that doesn't match up with the fact that it does fire to descendants through multiple levels.
I'm currently working around this by passing the individual fields through as attributes, but it means parent component(s) has to know more about the workings of the child component(s) than I'd like.