6

Using the overlayLibrary, I am having quite a headache to nicely bind the footer's buttons functions with a parents function and closing the modal.

What I currently have which work fine except it doesn't ever close the modal look like this :

Parent component

<aura:component>
<lightning:overlayLibrary aura:id="overlayLib" />
<aura:attribute name="overlay" type="Aura.Component" />

<aura:attribute name="currentObject" type="Object" />

<lightning:button label="New Object" onclick="{!c.click_NewObject}" />
</aura:component>

Parent Controller

({
    click_NewEvaluation: function (cmp, event, helper) {
        var newObject = {};
        cmp.set('v.currentObject', newObject);

        var modalBody, modalFooter;
        $A.createComponents([
            ['c:modalBody', { 'currentObject': cmp.getReference('v.currentObject') }],
            ['c:modalFooter', { 'saveFunction': cmp.getReference('c.callback_SaveFunction') }],
        ],
            function (components, status) {
                if (status === 'SUCCESS') {
                    modalBody = components[0];
                    modalFooter = components[1];

                    cmp.find('overlayLib').showCustomModal({
                        header: 'New Object',
                        body: modalBody,
                        footer: modalFooter,
                        showCloseButton: true
                    }).then(function (overlay) {
                        cmp.set('v.overlay', overlay);
                    });
                }
            }
        );
    },

    callback_SaveFunction: function (cmp, event, helper) {
        //Both command do not work.
        cmp.find('overlayLib').notifyClose();
        cmp.get('v.overlay').close();

        //This function simply uses the v.currentObject and 
        //save it and it works perfectly once called by the saveFunction.
        helper.saveObjects(cmp, helper);
    }
})

Modal Footer

<aura:component >
    <lightning:overlayLibrary aura:id="overlayLib" />
    <aura:attribute name="saveFunction" type="Aura.Action" />

    <lightning:button name="cancel" label="Cancel" onclick="{!c.handleCancel}" />
    <lightning:button name="save" label="Save" onclick="{!v.saveFunction}" />
</aura:component>

If I use the notifyClose function directly in the footer, it works fine as expected. If I use the close function on the overaly promise directly after the modal open, it works fine but once it is stored in the overlay attribute, I cannot make it work outside of the .then function scope.

I tried to use component events fired from the footer but it didn't work and I felt like I was going down a messy path. I feel like there's something rather simple I am missing as everything works perfectly and logically except closing the modal part.

Anybody has any insight on what might be going wrong or has come across example that deal with this? What I've found online is extremely simple and explaining the structure which already works but nothing a complex to work on.

Edit: The following answer actually provide a solution which can close the modal window by doing :

var popup = window.open(location, '_self', '');
popup.close();

I still think that this should not be done however and that there is a way to simply use the library functions in order to cleanly achieve this so I will leave the question open.

Edit 2: Debugging the notifyClose() function shows that the following warning is logged by the aura framework:

"There is no event definition for event 'notify', probably because there is no component to handling it."

So it seems the simply calling the notifyClose() function from the component that created the modal doesn't work because the required event it is firing is not registered. Adding the event manually is not an option either and I am not sure if that method would be the right one or if this is an unintended bug (or my code just has something missing, but I tried again with the most minimal implementation and I keep running into the same problem).

Edit 3: Debugging the close function that is called on the OverlayPanel object (returned by the promise when creating the modal) return a similar error:

"Access Check Failed! Component.method():'markup://ui:close' is not visible to 'markup://c:Parent_component {117:0}'."

That one, I am not sure what it means but I'll concentrate on that. It just seems that the method cannot be accessed and it has a weird feeling of something the Locker Service would do.

  • 1
    Hi @P. Lapointe. I am facing the same error you mentioned in 'Edit 3'. Were you able to find a way out? – Kanav Khurana Mar 25 '18 at 5:22
  • 1
    @KanavKhurana Its actually the whole framework that seem to have a problem when it comes to that. Debugging the whole thing show that there are still inconsistency between what is expected and what happen. A good example for that is when you use the notificationClose() function it doesn't trigger the normal close; the window disappear without any fade out. Following this pattern I simply encapsulated the library in a component and use an application event to close the component from within. Its not great architecture but its not a hack and should survive the test of time until things change. – P. Lapointe Mar 27 '18 at 17:52
  • Using this approach all my problems went away. It's about storing the promise return value as an Aura.Action attribute and then handling it as a promise. – Christian Szandor Knapp Nov 14 '18 at 0:06
2

It can be done via method on footer component.

You will need to save footer component as attribute in parent component.

Parent markup:

<aura:attribute name="footerCmp" type="Aura.Component"/>

When creating components, add following line in $A.createComponents callback:

cmp.set('v.footerCmp', components[1]);

Define a method in your footer component:

<aura:method name="closeModal" action="{!c.close}" />

And in footer component controller add "close" handler

close: function(component, event, helper) {
    component.find("overlayLib").notifyClose();
}

Now you can call "closeModal" method from callback_SaveFunction in your parent component like this:

cmp.get("v.footerCmp").closeModal();
1

Is there a reason you're setting "{! v.overlay }" manually?

OverlayLibrary seems to handle generating the modal overlay manually after you feed it the header and footer.

The same component responsible for overlayLib.showCustomModal() must be called to overlayLib.notifyClose(). It seems to me when you're binding the modal to your current component, the scope breaks.

Not that this would work, but this is what overlayLib wants you to do to close it.

cmp.get("v.overlay").find("overlayLib").notifyClose();

Check out my Service Components library here https://github.com/tsalb/sfdc-lightning-service-components, specifically the MessageService.modal() method. There's some generic code in there to help me spawn modals and it's been really easy to do some complex things.

  • Both the close function on the promise returned by the showCustomModal and the notifyClose close function from the library on the parent component that create the modal works. I will try that and see if it works. – P. Lapointe Jan 26 '18 at 20:47
  • Also, the reason why the v.overlay is being set manually is to keep the promise object returned by the function in order to interact with it. The returned promise is actually mentioned in Salesforce doc for the library and seemed natural to use (developer.salesforce.com/docs/atlas.en-us.lightning.meta/…) – P. Lapointe Jan 26 '18 at 20:48
  • While true you can interact with it from the generating component, it's a huge lift every time you want to generate a modal and a footer. It would seem to me more efficient to allow the modal components live and handle itself (including processing data you send it) and then send the post processed modal data in an application event (don't think component events work here) – tsalb Jan 26 '18 at 20:53
  • Component events do not work in that case indeed. The reason why I did that though is that since the footer and body do not interact with each other, I would need to fire an event from the footer button click, and handle it in the body. I simply though that using 2way data-binding from the parent and only have the modal content manipulate that data made the whole thing straightforward for any upcoming components. I'll investigate more as I haven't seen any best practice regarding those. – P. Lapointe Jan 26 '18 at 20:59
  • Do check out my service component pattern. It allows you to dynamically pass in a custom component as the body, and in the footer define your main action method which can be FROM your originating component or WITHIN the dynamically passed component body. – tsalb Jan 26 '18 at 21:02
0

If you want the ability to close the modal from itself, there's a way easier way doing this (for custom overlay comp.):

According the SF lightning comp. documentation you can declare another overlayLibrary on your custom overlay with the same aura:id on the parent comp. like this:

<lightning:overlayLibrary aura:id="overlayLib"/>

then, on your overlay component add this (as your button handler):

({
  handleCancel : function(component, event, helper) {
      //closes the modal or popover from the component
      component.find("overlayLib").notifyClose();
  },
})

Works like a charm.

  • I'll try it as soon as I can! That didn't work in the prior release unfortunately but I'll revisit this because I hate having a bit of a stiched up code base. – P. Lapointe Jul 3 '18 at 13:35
  • This doesn't work, actually. If you try it out you'll get the error: There is no event definition for event "notify", probably because there is no component to handling it. – smohyee Nov 26 '18 at 22:35
  • Are you sure I did all things right, like in the lighting documentation (add "<lightning:overlayLibrary aura:id="overlayLib"/>" in both components )? It's working for me. – Yonatan Goriachnick Nov 29 '18 at 9:03

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.