6

I have a parent component with the <lightning:overlayLibrary aura:id="overlayLib"/> component added.

The parents controller creates an instance of the modal component, assigns it to the body of the modal, and displays the modal using component.find('overlayLib').showCustomModal(). Nothing used in the footer.

Now, inside the modal component I have two buttons, 'Cancel' and 'Save'. Cancel just closes the modal, while Save makes a callout to the apex controller, then closes the modal.

Both button methods utilize component.find("overlayLib").notifyClose();... however, it only works for the Cancel button.

The Save button fires the notifyClose method from the then() function of the promise returned by the apex controller, and this seems to cause an error.

Console Log Error: There is no event definition for event "notify", probably because there is no component to handling it.

submitSaveObject : function(component, saveObj){
    var action = component.get('c.saveConfiguration');
    action.setParams({ "saveObj" : JSON.stringify(saveObj)});

    var actionPromise = new Promise(function (resolve, reject){

        action.setCallback(this, $A.getCallback( function(actionResult){
            var state = actionResult.getState();
            console.log('apex callback result: ');
            if (state === "SUCCESS"){
                var result = actionResult.getReturnValue();
                resolve(result);        
            } else if (state === "ERROR"){
                reject();
            }

        }));

        $A.enqueueAction(action);       
    });

    actionPromise.then(function(returnValue){
        alert('Save successful!');
        //this is throwing an error
        component.find("overlayLib").notifyClose();

    });
}

How can I successfully close the modal in such a way that it is enqueued after the asynch call to the apex controller?

  • I had this issue before and notifyClose(); doesn't seem to work anymore, I used close(); – EricSSH Nov 27 '18 at 17:33
  • Eric when I use component.find("overlayLib").close(); instead of notifyClose() I get a component error saying close() is not a function. – smohyee Nov 27 '18 at 17:36
  • Makes sense, this is my entire function that handles "cancel" handleCancel : function(component, event) { component.get("v.modalPromise").then( function (modal) { modal.close(); } ); }, – EricSSH Nov 27 '18 at 17:38
  • Interesting, and thanks for the tip... so is v.modalPromise assigned the promise returned when you first open the modal using component.find('overlayLib').showCustomModal()? – smohyee Nov 27 '18 at 17:43
  • So the problem I'm running into with your solution is my component.find('overlayLib').showCustomModal() is fired inside the $A.createComponent()method where I create the modal's body and also set the parameters to pass to the modal component. To use your method I'd need the promise returned by showCustomModal to be passed to the modal cmp as a param, so I can use the promises' then function to get the modal obj and use the close() method. But how can I pass that promise as a param when it is being generated within $A.createComponent()'s callback function? – smohyee Nov 27 '18 at 17:52
5
+100

Did you tried by adding a callback in .then

actionPromise.then($A.getCallback(function(returnValue){
        alert('Save successful!');
        //this is throwing an error
        component.find("overlayLib").notifyClose();

    }));
  • It worked! Such a simple solution too, thanks Manjot! So it appears that I can also just include the notifyClose in my initial getCallback when I make the callout to Apex... so I suppose this means that $A.getCallback has access to the component by default? – smohyee Dec 4 '18 at 15:21
  • I think it has something to do with modifying lightning component outside framework lifecycle. developer.salesforce.com/docs/atlas.en-us.lightning.meta/… – Manjot Singh Dec 4 '18 at 18:43
1

This is my solution for the modal, I did this a few months ago and I may be doing things here that you don't need to do anymore. My solution may be overly complicated for what you need

Inside my component markup I create my attributes

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

I also create custom components and inject them into the modal but I don't believe you need to do that

showModal: function(component, event, helper) {
    var modalBody;
    var modalFooter;

    $A.createComponents([
        ["c:DataTableNoteModalContent", {
            label:"modalContent",
            "textAreaInputValue" : component.getReference("v.textAreaInputValue")
        }],
        ["c:DataTableNoteModalFooter", {
            label:"modalFooter", 
            "rowID" : component.get("v.rowID"),
            "dataTableID" : component.get("v.dataTableID"),
            "buttonClickEvent":component.getReference("c.onButtonClick")
        }]],
        function(components, status) {
            if(status === "SUCCESS") {
                modalBody = components[0];
                modalFooter = components[1];
                var modalPromise = component.find("overlayLib").showCustomModal({
                    header: "Add",
                    body: modalBody, 
                    footer : modalFooter,
                    showCloseButton: true,
                    cssClass: "",
                    closeCallback: function(){
                        component.set("v.textAreaInputValue", component.get("v.textAreaInputPreviousValue"));
                    }
                });
                component.set("v.modalPromise", modalPromise);
            }
        }
    );
},

I have my button in my footer

<aura:attribute name="modalFooter" type="Aura.Component[]" >
<lightning:button aura:id="cancel" name="cancel" iconName="action:remove" 
 label="Cancel" onclick="{!c.handleCancel}" class="slds-button_neutral" />
</aura:attribute>

Then that calls my helper function

handleCancel : function(component, event) { 
    component.get("v.modalPromise").then(
        function (modal) {
            modal.close();
        }
    );
},

I hesitate to just code dump on you but I hope this can help

  • This is actually super helpful, but something seems strange: in your parent cmp you set modalPromise as an attribute for the parent cmp... But you then reference modalPromise in your modal cmp. I would have thought the modal's controller couldn't access the parent's attribute without it being specifically passed as a parameter! But I'll give it a try and see what happens... – smohyee Nov 27 '18 at 18:03
  • Sure enough, I get an error when attempting to call component.get("v.modalPromise").then() from within the modal cmp, since modalPromise isn't a defined attribute in the modal cmp, just the parent cmp. – smohyee Nov 27 '18 at 18:19
1

I am not a javascript wizard, but let me try putting my thought. You are not passing component to actionPromise method. Thus it doesn't have a component to fire .notifyClose();

As actionPromise happens asynchronously, somewhere in future, where component may or may not exists . it has no access to component

You have to pass it, Something like

submitSaveObject : function(component, saveObj){
    var action = component.get('c.saveConfiguration');
    action.setParams({ "saveObj" : JSON.stringify(saveObj)});

    var actionPromise = this.executeAction(component, accAction);

    actionPromise.then(function(returnValue){
        alert('Save successful!');
        //this is throwing an error
        component.find("overlayLib").notifyClose();

    });
}


executeAction: function(cmp, action, callback) {
    return new Promise(function(resolve, reject) {
        action.setCallback(this, $A.getCallback( function(actionResult){
            var state = actionResult.getState();
            console.log('apex callback result: ');
            if (state === "SUCCESS"){
                var result = actionResult.getReturnValue();
                resolve(result);        
            } else if (state === "ERROR"){
                reject();
            }

        }));
    }
}

Src: http://bobbuzzard.blogspot.com/2016/12/javascript-promises-in-lightning_30.html

  • Pranay, I agree with your assessment, but can you explain how your solution is different from my attempt above? Although you are passing the component to your executeAction method, you aren't passing it into the Promise inside that method. I believe if you ran your code the notifyClose() wouldn't do anything, like with my code. – smohyee Dec 4 '18 at 15:12

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.