I posted this question on Twitter and folks seemed to feel this wasn't expected behavior, so posting here to see if others have experienced\have better solution
I have three components. CMP A is a simple component that uses force:recordData so I can display on Contact detail page. On record update of CMP A (force:recorddata is async, so we can't use init) it creates CMP B which queries an apex controller to get back some values from a custom object for the Contact, and displays a select list of those values. That works fine.
On change of the select, I create CMP C, which is using values from CMP B to create a simple table:
var finalbody = [];
$A.createComponent(
"c:ProgramListSelector",
{
"program" : programsToDisplay,
"programMap" : component.get("v.programMap")
},
function(newListCMP, status, errorMessage){
//Add the new component to the body array
if (status === "SUCCESS") {
finalbody.push(newListCMP);
console.log('final body' + finalbody);
var parentCmp = component.find("newLists");
parentCmp.set("v.body", finalbody);
}
//Error Handling
}
);
}
If I look in the console, the first time I do this the success statement appears after that console.log('final body' + finalbody) statement and the component is not added to the page. Change the select again, and it works correctly every subsequent time.
The only way I found to correct it was to add CMP C as an aura dependency in CMP B – once I did that, it always worked, even the first time. But adding it is obviously a performance hit, so it feels like I shouldn't need to enforce that dependency?
One other thing that might be a factor – CMP C references an Apex Wrapper class – could that be why I need to force a dependency?
public with sharing class ContactListWrapper {
@AuraEnabled
public Boolean bSelected {get;set;}
@AuraEnabled
public String ListName {get;set;}
public ContactListWrapper(List__c Listso, Boolean b) {
pList = Listso;
}
}
Best Answer
So I had a great conversation with Kris Gray about this issue, and the core problem I was hitting is that the initial creation of my component is happening asynchronously because it has to retrieve the definition from the server. After that, it has the definition in the local cache, so it will happen synchronously.
In order to handle that possibility, he suggested I change my code to set the body variable of the component directly, rather than use a local variable. That way, it doesn't matter when they component creation is completed - it will just appear in the component body once the callback is completed.
The difference is pretty subtle, but it did mean I could remove the aura-dependency tag and it still worked. More generally, the big takeaway for me was more actions that I realized were happening async and I need to do a better job of handling that possibility