lightning:overlayLibrary and fire event issue

Its a special case where in the parent won't listen to child's event, for doing so you would need to pass the reference of parent controller's method while creating it.

So add attributes here:

$A.createComponent("c:TestChild", {},

like:

$A.createComponent("c:Child_213329", {
    "fromModal": component.getReference("c.fromModal")
},

With parent event would be binded and called whenever parent fires that specific event.

Note: I might be missing something; looks like some kind of bug in lightning:overlayLibrary It just doesn't works the first time popup is opened but works afterwards.


Passing parameter to parent:

Add params to the component event, TestChildController.js-

({
    callParent: function (component, event, helper) {
        var evt = component.getEvent('fromModal')
            .setParams({
                "message" : "A component event fired me. " +
                "It all happened so fast. Now, I'm here!" })
            .fire();
    }
});

Get parameter in TestParentController.js-

fromModal: function (component, event, helper) {
    var message = event.getParam("message");
    console.log('message from modal', message);
}

Complete code used for testing/demo.


For whatever reason, passing the handler as an attribute seems flaky (i.e., doesn't work on the first pop-up). But if I call addEventHandler on the component in the $A.createComponent callback instead, that seems to work every time.

More info here: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cb_dynamic_handler.htm


From my research and playing around with this, I have a slight alteration/suggestion to Raul's answer (which was really helpful and I've up-voted by the way).

There are two solutions. 1: do what Raul suggested by make sure you create the components at least once in the component's init function (you don't need to open the modal. Just create the components). This way, the event will register even on the first open.

2: A better solution is to use an application event. This allows you to not only handle the event in the parent, but also from the modal footer to the modal body and THEN in the parent. This also negates the need for an init function at all if you don't otherwise need one.

for example:

the event:

<!-- exampleEvent.evt -->
<aura:event type="APPLICATION">
    <aura:attribute name="message" type="String"/>
    <aura:attribute name="number" type="Integer"/>
</aura:event>

in the parent mark up:

<!-- parent.cmp -->
<aura:handler event="c:exampleEvent" action="{!c.handleEventFromModalBody}" />
<lightning:overlayLibrary aura:id="overlayLib"/>
<lightning:button label="open modal" variant="success" onclick="{!c.openModal}" />

Note: DON'T give the handler a name or it won't work with an application event.see here)

The handler for an application event won’t work if you set the name attribute in . Use the name attribute only when you’re handling component events. :

In the modal footer markup (it's fine to have a name for aura:registerEvent):

<!-- ModalFooter.cmp -->
<aura:registerEvent name="addToCartEvent" type="c:exampleEvent"/>
<lightning:button label="Add to Cart" variant="success" onclick="{!c.addToCart}"/>

In the modal footer js controller: (Note: notice the different syntax for getting the aura:register event for an application event. You have to use $A.get("e.c:eventName") rather than component.getEvent("eventName") )

/* method in ModalFooterController.js */
addToCart : function(component, event, helper) {
    var addToCartEvent = $A.get("e.c:exampleEvent");
    addToCartEvent.fire();
}

In the modal body markup (You need the lightning:overlayLibrary here to close the modal):

<!-- ModalBody.cmp -->
<aura:handler event="c:exampleEvent" action="{!c.handleEventFromFooter}" />
<aura:registerEvent name="addToCartEvent" type="c:exampleEvent"/>
<lightning:overlayLibrary aura:id="overlayLib"/>

In the modal body js controller: (This code checks that event came from the footer, then fires it's own event with some data attached to it. The data could be a list of accounts or a custom object or whatever you want, as long as you can store it in an aura:attribute)

/* method in ModalBodyController.js */
handleEventFromFooter : function(component, event, helper) {
    var source = event.getSource();

    if (source.getName() ===  'cModalBody') {
        console.log('event handled in modal body');

        var addToCartEvent = $A.get("e.c:exampleEvent");
        addToCartEvent.setParams({
            message: 'this event message was defined just before closing the modal',
            number: 5
        });

        addToCartEvent.fire();

        // close the modal
        component.find('overlayLib').notifyClose();
    }
}

Finally, in the parent component js controller: (This code checks that the event came from the body, rather than the footer, then executes whatever you want it to)

/* method in parentController.js */
handleEventFromModalBody: function (component, event, helper) {
    var source = event.getSource();

    if (source.getName() ===  'cModalBody') {
        console.log('handled in parent.');
        //your code here
    }
}

Hope this helped someone out there!

Cheers, Dunks