4

UPDATE: Resolved by updating the Lightning Component Bundles being created to API version 43.0

ALSO: For future reference, the 8th line of the second code block (var el = els[i];) should instead be let el = els[i]; to work properly.

I have code that creates a number of components within a loop. Sometimes when creating these components, I want to dynamically add an event handler.

Here is a working example of this:

createOneComponent : function(component, attributes, name) {
    $A.createComponent(
        name,
        attributes,
        function(el, status, err) {
            if (status === "SUCCESS") {
                var body = component.get("v.body");

                // ADDING HANDLER HERE
                if (true) {
                    el.addEventHandler("c:AF_LinkEvent", function(event) {
                        // DO THINGS HERE
                    });
                }
                body.push(el);
                component.set("v.body", body);
            } else if (status === "ERROR") {
                console.log("Error: " + err);
            }
        }
    );
}

However, during the course of development I discovered that for components of different complexity, these callbacks weren't always coming back in order, meaning components were being placed on the page out of order. To solve this, I instead implemented createComponents, like so:

createManyComponents : function(component, compList) {
    $A.createComponents(
        compList,
        function(els, status, err) {
            if (status === "SUCCESS") {
                var body = component.get('v.body');
                for (var i=0; i<els.length; i++) {
                    var el = els[i];

                    // ADDING HANDLER HERE
                    if (true) {
                        el.addEventHandler('c:AF_LinkEvent', function(event) {
                            // DO THINGS HERE
                        });
                    }
                    body.push(el);
                }
                component.set('v.body', body);
            } else if (status === "ERROR") {
                console.log("Error: " + err);
            }
        }
    );
}

When I use this method in this context, I get the following error:

This page has an error. You might just need to refresh it. Error in $A.getCallback() [el.addEventHandler is not a function] Callback failed: aura://ComponentController/ACTION$getComponent Failing descriptor: {c:AF_PageWrapper}

It's also worth noting that when I log el in either example, I get objects with different attributes. So it seems like createComponent and createComponents return a different type of object, with createComponent returning a proper DOM object while createComponents returns something else.

Is this working as intended? Have I missed something? Can anyone suggest a workaround for this problem? Thanks in advance.

  • You could just add a listener to the {!v.body} component and get attributes to tell you which component was fired event.getSource().get("v.name") or event.getSource().get("v.id") – Caspar Harmer Sep 5 '18 at 18:10
0

Thanks for the feedback. Ultimately, what was holding me back was the Lightning Component Bundles I was creating in createComponent and createComponents were an old API version. When creating components of API versions before 43.0, createComponent and createComponents do not return the same object types. Once I updated the components to 43.0, addEventHandler started working exactly the way I wanted it to (meaning the second example code block now works).

0

The way createComponent and createComponents methods are supposed to work is that if the component is in the same namespace, you can add event handlers as normal, but if they're in different namespaces, some methods, like addEventHandler, won't exist. The difference is that in the same namespace, you get a SecureComponent that has more methods available, while if it's a different namespace, you get a SecureComponentRef, which does not have some methods available. There may have been glitches in previous versions of Locker Service, but they should now appear/not appear correctly based on the security context.

Here's the general difference between the two:

    // This works okay, my namespace
    $A.createComponent("c:myCustomComponent", {}, function(newComponent) {
        newComponent.addEventHandler("c:genericAppEvent", function(component, event, helper) {
            console.log("Rejoice, for we have received a genericAppEvent, and can handle it correctly");
        });
        component.set("v.body", [newComponent]);
    });
    // Throws error, not my namespace
    $A.createComponent("ui:inputText", {}, function(newComponent) {
        newComponent.addEventHandler("c:genericAppEvent", function(compoent, event, helper) {
            console.log("We can never reach here, because addEventHandler was not a function");
        });
        component.set("v.body", [newComponent]);
    });

You'll need to make sure that the function exists before you call it to avoid this error.

  • @iwhitt567 I deleted the previous answer which was partially/totally incorrect with an example of why there'd be a difference between when addEventHandler should appear and when it should not. Does that help? As you noted in your answer, it may have been API version related, but the standard answer should be that you can't attach listeners to namespaces you don't own. – sfdcfox Oct 8 '18 at 20:19
  • Literally the only difference between the two codeblocks you posted here are that one is created a custom component and one is creating a ui component. But all of my code has been dealing with custom components, so I don't see how this is relevant? Also I've been using createComponents, not createComponent. Also, the answer still seems to imply that this pattern doesn't work, but I provided a working example in the chat. Did you ever try to run that? – iwhitt567 Oct 9 '18 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.