1

I referred the following link https://alinimer.wordpress.com/2013/02/25/open-pages-and-forms-in-modal-dialog-for-sharepoint-2013 to create a modal pop up .

I am able to create a pop up for the new form on button click with all the sharepoint list items as required. But after filling the values , on clicking Cancel button , I get an error in the console Cannot read property 'cancelPopUp' of null and the pop up window does not close on clicking cancel. Also on clicking the save button , the pop up does not close.

Can anyone please help on this?

4
  • Did you download the function from the link shared at the end of the blog post? (That is, this.) It sounds a bit like there would be a typo in the latter function
    – moe
    Jun 15, 2017 at 5:31
  • Can you please elaborate ? Also the link that you have provided is not secure , can you please share another link?
    – kirti
    Jun 15, 2017 at 5:47
  • I have given the link which is shared at the end of the blog post you have linked in your original question. This link provides the .JS-file you should use for creating the popup. If you have manually written the .JS by now, I suspect there could be something missing or something wrong which causes the issues.
    – moe
    Jun 15, 2017 at 5:49
  • Thanks Moe for the clarification on above, but I think there is no issue with the .js file , The issue is in the Sharepoint OOTB pop up . When I click on the "Cancel" button, it then shows error in the NewForm.aspx
    – kirti
    Jun 15, 2017 at 5:59

2 Answers 2

2

This is a repost of my answer from StackOverflow.

I'm going to assume this problem is related to cross site scripting restrictions. Cross site scripting security features of modern browsers disallow a few things, one of which is the use of window.frameElement from within the framed window. This is a read-only property on the window object and it becomes set to null (or with IE, it actually throws an exception when you try to access it). The ordinary Cancel event handlers in the modal dialog conclude with a call to window.frameElement.cancelPopup(). This will fail of course. The ordinary Save handler where the Save worked on the server side results in SharePoint sending back a single line as the replacement document, which is a scriptlet to call window.frameElement.commitPopup(). This also will not work, and it's a real pain to overcome because the page has been reloaded and there is no script available to handle anything. XSS won't give us access to the framed DOM from the calling page.

In order to make a cross domain hosted form work seamlessly, you need to add script to both the page that opens the dialog and the framed page. In the page that opens the dialog, you set the message listener as suggested by Ajay's answer on Stack Overflow. I reproduce that here:

function listener(event) {
  //alert(event.data);
  if (event.data == 'Cancel') {
    SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancel clicked');
  }
  else {
    SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, event.data);
  }
}

if (window.addEventListener) {
  addEventListener("message", listener, false)
} else {
  attachEvent("onmessage", listener)
}

In the framed form page, you need something like below:

(function() {
    $(document).ready(function() {
        var frameElement = null;
        // Try/catch to overcome IE Access Denied exception on window.frameElement
        try {
            frameElement = window.frameElement;
        } catch (Exception) {}

        // Determine that the page is hosted in a dialog from a different domain
        if (window.parent && !frameElement) {
            // Set the correct height for #s4-workspace
            var frameHeight = $(window).height();
            var ribbonHeight = $('#s4-ribbonrow').height();
            $('#s4-workspace').height(frameHeight - ribbonHeight);

            // Finds the Save and Cancel buttons and hijacks the onclick
            function applyClickHandlers(theDocument) {
                $(theDocument).find('input[value="Cancel"]').removeAttr('onclick').on('click', doTheClose);
                $(theDocument).find('a[id="Ribbon.ListForm.Edit.Commit.Cancel-Large"]').removeAttr('onclick').removeAttr('href').on('click', doTheClose);
                $(theDocument).find('input[value="Save"]').removeAttr('onclick').on('click', doTheCommit);
                $(theDocument).find('a[id="Ribbon.ListForm.Edit.Commit.Publish-Large"]').removeAttr('onclick').removeAttr('href').on('click', doTheCommit);
            }

            // Function to perform onclick for Cancel
            function doTheClose(evt) {
                evt.preventDefault();
                parent.postMessage('Cancel', '*');
            }

            // Function to perform onclick for Save
            function doTheCommit(evt) {
                evt.preventDefault();

                if (!PreSaveItem()) return false;
                var targetName = $('input[value="Save"]').attr('name');
                var oldOnSubmit = WebForm_OnSubmit;
                WebForm_OnSubmit = function() {
                    var retVal = oldOnSubmit.call(this);
                    if (retVal) {
                        var theForm = $('#aspnetForm');
                        // not sure whether following line is needed,
                        // but doesn't hurt
                        $('#__EVENTTARGET').val(targetName);
                        var formData = new FormData(theForm[0]);
                        $.ajax(
                        {
                            url: theForm.attr('action'),
                            data: formData,
                            cache: false,
                            contentType: false,
                            processData: false,
                            method: 'POST',
                            type: 'POST', // For jQuery < 1.9
                            success: function(data, status, transport) {
                                console.log(arguments);
                                // hijack the response if it's just script to
                                // commit the popup (which will break)
                                if (data.startsWith('<script') &&
                                    data.indexOf('.commitPopup()') > -1)
                                {
                                    parent.postMessage('OK', '*');
                                    return;
                                }

                                // popup not being committed, so actually
                                // submit the form and replace the page.
                                theForm.submit();
                            }
                        }).fail(function() {
                            console.log('Ajax post failed.');
                            console.log(arguments);
                        });
                    }

                    return false;
                }
                WebForm_DoPostBackWithOptions(
                    new WebForm_PostBackOptions(targetName,
                                                "",
                                                true,
                                                "",
                                                "",
                                                false,
                                                true)
                );
                WebForm_OnSubmit = oldOnSubmit;
            }

            applyClickHandlers(document);
        }
    });
})();

This solution makes use of the jQuery library, which our organization uses extensively. It is our preferred framework (chosen by me). I'm sure someone very clever could rewrite this without that dependency, but this is a good starting point. I hope someone finds it useful, as it represents a good two days work. Some things to note:

SharePoint does a postback on all sorts of events on the page, including putting the page into edit mode. Because of this, it makes more sense to trap the specific button clicks, both on the form and in the ribbon, rather than wholesale redefinition of, for example, the global WebForm_OnSubmit function. We briefly override that on a Save click and then set it back.

On any Save click event, we defeat the normal posting of the form and replace that with an identical POST request using AJAX. This allows us to discard the returned scriptlet when the form was successfully posted. When the form submission was not successful, perhaps because of blank required values, we just post the form properly to allow the page to be updated. This is fine, since the form will not have been processed. An earlier version of this solution took the resulting HTML document and replaced all of the page contents, but Internet Explorer doesn't like this.

The FormData api allows us to post the form as multipart-mime. This api has at least basic support in all modern browsers, and there are workarounds for older ones.

Another thing that seems to fail in the cross domain hosted dialog is the scrolling of the content window. For whatever reason, the height is not set correctly on the div with id s4-workspace, so we also set that in the solution.

EDIT: Almost forgot. You may also need to add this control to your framed ASPX page, which can be done with SharePoint Designer:

<WebPartPages:AllowFraming runat="server">
1
  • Thanks for editing my answer. How about some up votes?
    – flayman
    Apr 25, 2018 at 9:40
0

When submitting (=saving) a form, it should fire

window.frameElement.commitPopup(); return false;

and when closing a form, it should fire

window.frameElement.cancelPopUp(); return false;

Of these, commitPopup() returns SP.UI.DialogResult.OK, which shall do the save-related refresh for your page. cancelPopUp() returns SP.UI.DialogResult.cancel, which is the latter condition in your CloseDialogCallBack function.

I'm suspecting the Save and Close elements of your form don't have the correct onclick-actions defined in them.


Edit: Confirm, that you have included the related .js-files on the page running your app:

<SharePoint:ScriptLink ID="ScriptLink1" Name="init.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
<SharePoint:ScriptLink ID="ScriptLink2" Name="sp.init.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
<SharePoint:ScriptLink ID="ScriptLink3" Name="sp.runtime.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
<SharePoint:ScriptLink ID="ScriptLink4" Name="sp.core.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
<SharePoint:ScriptLink ID="ScriptLink5" Name="sp.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
<SharePoint:ScriptLink ID="ScriptLink7" Name="sp.ui.dialog.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
4
  • Thanks Moe but as you must have seen in the blog, I haven't provided the code for Save and Cancel , these are inbuilt functionalities of Share point new list form . Also if I have to provide the code for Save and Cancel , can you let me know where to add it as I can neither add it in default.aspx nor in the opnInDialog as per the blog ?
    – kirti
    Jun 15, 2017 at 7:20
  • @kirti been thoroughly trying to troubleshoot your case and really can't come up with anything that would make sense. There's likely nothing wrong in the JS then, assuming you used the exact code from the source (alternatively available here in GitHub), so it's possibly related to the available JS functions in your hosted app. I've edited the answer to give one more thing to check.
    – moe
    Jun 15, 2017 at 7:56
  • I am still getting the same error :( I have added all these JS files but no success!!
    – kirti
    Jun 15, 2017 at 9:16
  • I have the same issue when I use the modal dialog to load a page that resides on a different domain. I wonder whether the OP is also doing this. When the dialog loads a cross-domain page a number of things do not work correctly. This will no doubt be caused by browser features that avoid XSS issues. I am seeing that window.frameElement is null. I am currently investigating possible workarounds.
    – flayman
    Jan 30, 2018 at 14:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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