[SalesForce] Problems “find”ing a dynamically created child component

I'm just getting started working with dynamically creating child components and am having problems "find"ing them to do things like calling a method in them.

If the component is created statically in the parent's markup, I can find it just fine by its aura:id. But when I create it dynamically, I can't find it.

Below is the code for the app, parent, static child, and dynamic child. The problem can be reproduced by pressing the "Call Static Child Method" button and observing console log message where the static child's method got called. Then press the "Create Dynamic Child" button and observe the dynamic child gets created. Then press the "Call Dynamic Child Method" button and observe trying to find the dynamic child in the parent, or the dynamic div in the parent, both return undefined.

I've tested communicating with other components using events, but I also want to be able to use methods also.

Apologies in advance for such a long post, but wanted to show all the code in case I'm doing something really wrong in creating these child components. Thanks for any help!

App component (testFindDynamic_App):

<aura:application >
    <c:testFindDynamic_Parent />
</aura:application>

Parent component (testFindDynamic_Parent):

<aura:component >
    <aura:handler name="init"    action="{!c.doInit}"    value="{!this}"/>
    <aura:handler name="destroy" action="{!c.doDestroy}" value="{!this}"/>

    <aura:attribute name="dynamicChildCreated" type="Boolean" default="false"/>

    <lightning:button label="Call Static Child Method"
                      onclick="{!c.buttonCallStaticChildMethodPressed}"/>
    <hr/>

    <lightning:button label="Create Dynamic Child"
                      onclick="{!c.buttonCreateDynamicChildPressed}"/><br/>

    <lightning:button label="Call Dynamic Child Method"
                      onclick="{!c.buttonCallDynamicChildMethodPressed}"/><br/><br/>

    <hr/>

    <div aura:id="divStaticChild">
        <c:testFindDynamic_StaticChild aura:id="staticChild"/>
    </div>

    <div aura:id="divDynamicChild">
    </div>

</aura:component>

Parent controller (testFindDynamic_ParentController):

({
    doInit : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_ParentController.doInit) Called.");
    },

    doDestroy : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_ParentController.doDestroy) Called.");
    },

    buttonCreateDynamicChildPressed : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCreateDynamicChildPressed) Called.");

        if (component.get("v.dynamicChildCreated") == false) {
            $A.createComponent("c:testFindDynamic_DynamicChild",
                               {"aura:id" : "dynamicChild"},
                               function createComponentCallback(newComponent, status, errorMessage) {
                if (status === "SUCCESS") {
                    var divDynamicChild     = component.find("divDynamicChild");
                    var divDynamicChildBody = divDynamicChild.get("v.body");
                    divDynamicChildBody.push(newComponent);
                    divDynamicChild.set("v.body", divDynamicChildBody);

                    component.set("v.dynamicChildCreated", true);

                }
                else if (status === "INCOMPLETE") {
                    console.log("DEBUG: (testFindDynamic_ParentController.createComponentCallback) status = INCOMPLETE; No response from server or client is offline.");
                }
                else if (status === "ERROR") {
                    console.log("DEBUG: (testFindDynamic_ParentController.createComponentCallback) status = ERROR; errorMessage = " + errorMessage);
                }
                else {
                    console.log("DEBUG: (testFindDynamic_ParentController.createComponentCallback) Unhandled status; status = " + status);
                }
            })
        };
    },

    buttonCallStaticChildMethodPressed : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallStaticChildMethodPressed) Called.");

        var staticChild = component.find("staticChild");
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallStaticChildMethodPressed) staticChild = " + staticChild);

        staticChild.staticChildMethod();
    },

    buttonCallDynamicChildMethodPressed : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) Called.");

        if (component.get("v.dynamicChildCreated") == true) {
            console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside the parent =====");
            var dynamicChild = component.find("dynamicChild");
            console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = " + dynamicChild);

            console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside dynamic div =====");
            var divDynamicChild = component.find("divDynamicChild");
            var dynamicChild    = divDynamicChild.find("dynamicChild");
            console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) divDynamicChild = " + divDynamicChild);
            console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = "    + dynamicChild);

            // Both the above finds result in no dynamic child component being found.

            //dynamicChild.dynamicChildMethod();
        }
    },

})

Static child component (testFindDynamic_StaticChild):

<aura:component>
    <aura:handler name="init"    action="{!c.doInit}"    value="{!this}"/>
    <aura:handler name="destroy" action="{!c.doDestroy}" value="{!this}"/>

    <aura:method name="staticChildMethod" action="{!c.staticChildMethod}" description="Static Child Method"/>

    <h1>I AM A STATIC CHILD</h1>
</aura:component>

Static child controller (testFindDynamic_StaticChildController):

({
    doInit : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_StaticChildController.doInit) Called.");
    },

    doDestroy : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_StaticChildController.doDestroy) Called.");
    },

    staticChildMethod : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_StaticChildController.staticChildMethod) Called.");
    },

})

Dynamic child component (testFindDynamic_DynamicChild):

<aura:component >
    <aura:handler name="init"    action="{!c.doInit}"    value="{!this}"/>
    <aura:handler name="destroy" action="{!c.doDestroy}" value="{!this}"/>

    <aura:method name="dynamicChildMethod" action="{!c.dynamicChildMethod}" description="Dynamic Child Method"/>

    <h1>I AM A DYNAMIC CHILD</h1>
</aura:component>

Dynamic child component (testFindDynamic_DynamicChildController):

({
    doInit : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_DynamicChildController.doInit) Called.");
    },

    doDestroy : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_DynamicChildController.doDestroy) Called.");
    },

    dynamicChildMethod : function(component, event, helper) {
        console.log("DEBUG: (testFindDynamic_DynamicChildController.dynamicChildMethod) Called.");
    },

})

Log snippet of finding static and dynamic children:

DEBUG: (testFindDynamic_StaticChildController.doInit) Called.
DEBUG: (testFindDynamic_ParentController.doInit) Called.
DEBUG: (testFindDynamic_ParentController.buttonCallStaticChildMethodPressed) Called.
DEBUG: (testFindDynamic_ParentController.buttonCallStaticChildMethodPressed) staticChild = markup://c:testFindDynamic_StaticChild {32:0} {staticChild}
DEBUG: (testFindDynamic_StaticChildController.staticChildMethod) Called.
DEBUG: (testFindDynamic_ParentController.buttonCreateDynamicChildPressed) Called.
DEBUG: (testFindDynamic_DynamicChildController.doInit) Called.
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) Called.
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside the parent =====
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = undefined
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside dynamic div =====
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) divDynamicChild = markup://aura:html {36:0} {divDynamicChild}
DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = undefined

(end of post)

Best Answer

To attach the component(child) under a component(parent), you are doing following:

1.Create the component using $A.createComponent

2.Finding the parent component to attach the dynamic child using component.find(parentId) which returns the component instance(Aura.Component)

3.Then getting the body of the above, then adding dynamic child to the body and setting the body.

So, child component(dynamic) needs to be inside the body of the parent component to be rendered in the view.

Also remember, you are attaching the dynamically created component to the body of the <div aura:id="divDynamicChild"></div> not to the actualy body of the component <c:testFindDynamic_Parent>.

So, you need to go deeper to get the actual instance of the dynamic child components that was created.

What you need to do is:

1.First, find the parent component instance using component.find("parentId").

2.Get the parent cmp body because that's where the child cmp resides using parentCmp.get("v.body").

2.Since the child component resides in the 0th index of the parent component's body attribute. You need to do this: parentBody[0].find("dynChildId").

var parentCmp = component.find("parentId");
var parentBody = parentCmp.get("v.body");
var childcmp = parentBody[0].find("dynChildId"); // returns the child component instance.

Now, your method buttonCallDynamicChildMethodPressed method will look like this:

buttonCallDynamicChildMethodPressed : function(component, event, helper) {
    console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) Called.");

    if (component.get("v.dynamicChildCreated") == true) {
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside the parent =====");
        var divDynamicChild = component.find("divDynamicChild");
        var divDynamicChildBody = divDynamicChild.get("v.body");
        var dynamicChild = divDynamicChildBody[0].find("dynamicChild");
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = " + dynamicChild);

        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) ===== Try to find dynamic child inside dynamic div =====");
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) divDynamicChild = " + divDynamicChild);
        console.log("DEBUG: (testFindDynamic_ParentController.buttonCallDynamicChildMethodPressed) dynamicChild = "    + dynamicChild);
    }
},

Here's an sample app:

child.cmp

<aura:component access="global">
    <aura:attribute name="message" type="String"/>
    <div>{!v.message}</div>
</aura:component>

parent.app

<aura:application >
    <aura:dependency resource="markup://c:child" />

            <ui:button label="greetings" 
                       class="slds-button slds-button--neutral"
                       labelClass="label"
                       press="{!c.greetings}"/>


    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <p>Dynamically created button</p>
    <div aura:id="test"></div>
</aura:application>

parentcontroller.js

({

    doInit : function(cmp) {
        $A.createComponent(
            "c:child",
            {
                "aura:id":"test1",
                "message":"Good morning"
            },
            function(newChild){
                if (cmp.isValid()) {
                    var parent = cmp.find("test");
                    var body = parent.get("v.body");
                    body.push(newChild);
                    parent.set("v.body", body);
                }
            }
        );

        $A.createComponent(
            "c:child",
            {
                "aura:id":"test2",
                "message":"Have a nice day"
            },
            function(newChild){
                if (cmp.isValid()) {
                    var parent = cmp.find("test");
                    var body = parent.get("v.body");
                    body.push(newChild);
                    parent.set("v.body", body);
                }
            }
        );
    },

    greetings : function(cmp) {
        var d = cmp.find("test").get("v.body");
        console.log("child 1",d[0].find("test1").get("v.message"));
        console.log("child 2",d[1].find("test2").get("v.message"));
    }

})