Popup Controller Component - Disable 'X', Escape key and hook dialog events

Ever want to detect when the ‘X’ or Escape Key is clicked/pressed to initiate a dialog close - or better yet, disable it completely? Ever want to hook open/close events or run some logic that would stop a dialog from closing in certain situations?

I’ve had this need on many occasions and decided it was time to polish up the custom component I had written. There are many posts in the community regarding this topic so I thought I would share.

Enter, the Popup Controller custom component.

The component hooks the jQuery dialog and its events and invokes snippets that you define when various events occur. It also contains a feature for “hiding the ‘X’” and “disabling the escape key from closing the dialog.” Each popup will have its own Popup Controller so you can vary functionality from popup to popup.

Please note:

  1. This has not been tested yet in every possible situation (e.g. popups in popups). That said, it should work in all cases and if you do find a problem, please let me know.
  2. This is written for Skuid 7.x (Banzai) but very minor modifications (builders.js) would be needed to support Skuid 6.x.

To use it:

  1. Create a new Component pack with a prefix of “myns” and Component Pack Label of “My Namespace Components”
  2. Upload the zip file from here to replace the default Static Resource created by Skuid for the pack
  3. Place the Popup Controller Component as the first component on any popup
  4. Set the Popup Controller component properties as appropriate for your situation

You of course can modify the component pack to fit your specific namespace/module name, etc.

Below is a sample XML page that demonstrates the functionality. To use the sample page, complete steps #1, #2 & #3 from above, then create a new skuid page and copy/paste the XML below.

Feedback, issues, etc. appreciated. Enjoy!

Sample Page XML (uses Account object, standard fields, etc.)

<skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="true" showheader="true" tabtooverride="Account">   <models>
      <model id="Account" limit="100" query="true" createrowifnonefound="false" sobject="Account" adapter="" type="">
         <fields>
            <field id="Name"/>
            <field id="CreatedDate"/>
         </fields>
         <conditions>
            <condition type="param" value="id" field="Id" operator="=" enclosevalueinquotes="true" novaluebehavior="noquery"/>
         </conditions>
         <actions/>
      </model>
   </models>
   <components>
      <pagetitle model="Account" uniqueid="sk-3ro7Me-67">
         <maintitle>
            <template>{{Model&#46;labelPlural}}</template>
         </maintitle>
         <subtitle>
            <template>Home</template>
         </subtitle>
         <actions>
            <action type="multi" label="Edit in Popup With X" icon="sk-icon-edit">
               <actions>
                  <action type="showPopup">
                     <popup title="New Popup" width="90%">
                        <components>
                           <myns__mynspopupcontroller uniqueid="sk-3roGN_-93" oncreate="onCreateSnippet" onopen="onOpenSnippet" onbeforeclose="onBeforeCloseSnippet" onclose="onCloseSnippet" hideclose="false" disableescape="false"/>
                           <basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="Account" buttonposition="" uniqueid="sk-3royd7-385" mode="read">
                              <columns>
                                 <column width="100%">
                                    <sections>
                                       <section title="Section A" collapsible="no">
                                          <fields>
                                             <field id="Name"/>
                                          </fields>
                                       </section>
                                    </sections>
                                 </column>
                              </columns>
                           </basicfieldeditor>
                           <pagetitle uniqueid="sk-3rp3OG-403" model="Account">
                              <actions>
                                 <action type="multi" label="Save" icon="sk-icon-save">
                                    <actions>
                                       <action type="save"/>
                                    </actions>
                                 </action>
                                 <action type="multi" label="Cancel" icon="sk-icon-cancel">
                                    <actions>
                                       <action type="closeTopmostPopup"/>
                                    </actions>
                                 </action>
                              </actions>
                              <conditions>
                                 <condition type="contextrow" field="Id" mergefield="Id" operator="="/>
                              </conditions>
                           </pagetitle>
                        </components>
                     </popup>
                  </action>
               </actions>
            </action>
            <action type="multi" label="Edit in Popup Without X" icon="sk-icon-edit">
               <actions>
                  <action type="showPopup">
                     <popup title="New Popup" width="90%">
                        <components>
                           <myns__mynspopupcontroller uniqueid="sk-3roGN_-93" oncreate="onCreateSnippet" onopen="onOpenSnippet" onbeforeclose="onBeforeCloseSnippet" onclose="onCloseSnippet" hideclose="true" disableescape="true"/>
                           <basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="Account" buttonposition="" uniqueid="sk-3royd7-385" mode="read">
                              <columns>
                                 <column width="100%">
                                    <sections>
                                       <section title="Section A" collapsible="no">
                                          <fields>
                                             <field id="Name"/>
                                          </fields>
                                       </section>
                                    </sections>
                                 </column>
                              </columns>
                           </basicfieldeditor>
                           <pagetitle uniqueid="sk-3rp3OG-403" model="Account">
                              <actions>
                                 <action type="multi" label="Save" icon="sk-icon-save">
                                    <actions>
                                       <action type="save"/>
                                    </actions>
                                 </action>
                                 <action type="multi" label="Cancel" icon="sk-icon-cancel">
                                    <actions>
                                       <action type="closeTopmostPopup"/>
                                    </actions>
                                 </action>
                              </actions>
                              <conditions>
                                 <condition type="contextrow" field="Id" mergefield="Id" operator="="/>
                              </conditions>
                           </pagetitle>
                        </components>
                     </popup>
                  </action>
               </actions>
            </action>
         </actions>
      </pagetitle>
      <basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="Account" buttonposition="" uniqueid="sk-3uLhmh-94" mode="read">
         <columns>
            <column width="100%">
               <sections>
                  <section title="Section A" collapsible="no">
                     <fields>
                        <field id="Name"/>
                        <field id="CreatedDate"/>
                     </fields>
                  </section>
               </sections>
            </column>
         </columns>
      </basicfieldeditor>
   </components>
   <resources>
      <labels/>
      <css/>
      <javascript>
         <jsitem location="inline" name="popupSnippets" cachelocation="false" url="">(function(skuid){
var $ = skuid&#46;$;
skuid&#46;snippet&#46;registerSnippet('onCreateSnippet', function(eventArg) {
    window&#46;alert('onCreateSnippet');
});
skuid&#46;snippet&#46;registerSnippet('onOpenSnippet', function(eventArg) {
    window&#46;alert('onOpenSnippet');
});
skuid&#46;snippet&#46;registerSnippet('onBeforeCloseSnippet', function(eventArg) {
    window&#46;alert('onBeforeCloseSnippet - wasXButton=' + eventArg&#46;wasXButton + ' / wasEscapeKey=' + eventArg&#46;wasEscapeKey);    
    var prohibitClose = eventArg&#46;wasXButton || eventArg&#46;wasEscapeKey;
    &#47;&#47; returning false will prohibit close
    return !prohibitClose;
});
skuid&#46;snippet&#46;registerSnippet('onCloseSnippet', function(eventArg) {
    window&#46;alert('onCloseSnippet');
});
})(skuid);</jsitem>
      </javascript>
   </resources>
   <styles>
      <styleitem type="background" bgtype="none"/>
   </styles>
</skuidpage>

This thing is awesome!!! Why isn’t this just the way popups work!

Barry, you are the man!

I mean, I’ve done some of what you’ve done, but this has way more polish on it for sure. This is a way complete solution for the popup. All the bells and whistles included!

Still can’t believe I missed this.

Glad you like it Pat.  Its worked out very well for me, enjoy!

Thanks Barry.  Really appreciate your sharing here. 

This idea is listed as implemented. Does this mean this will be in the next release?

Also, when is the next release going to be?

Or was the idea marked implemented because Barry implemented it?  I am also lost here.

If I were to guess, I think it was marked as implemented based on my posting.  I agree that the workaround provided in this post is implemented but the features that this workaround is attempting to solve for (or at least make it easier) haven’t been implemented.  There are various posts through the community requesting features to be added to popups (disable ‘X’,’ disable escape, control vertical positioning, run snippets before/after, etc).  The custom component here provides hooks for all of that, just not in a stock feature.

Just making an educated guess here but I don’t think this post being marked as implemented is an indication that the features it provides are planned for the next release (although I could be wrong).

Anna - Can you confirm?  Possibly each of the various individual posts in the community asking for these individual features are being tracked internally separately?  Or should we create a new post asking for the features so that they might be added at some point?

Not sure why this is posted in “implemented”, but “After Close Actions” are in Update 7, we’re in the final testing phases of that release right now.

Awesome!!!

ETA on update 7?

Don’t quote me, but early next week.

Awesome news Ben, having “After Close” could solve for several of the use cases.

Will it provide a mechanism to detect if the dialog was closed via ‘X’ or ‘Escape’ vs some other means?


The actions will be triggered however the popup is closed, but will not differentiate on the method of closing.

Thanks for the update Ben.  

Are there plans to add ability to disable ‘x’/escape, have different events sequences for ‘cancel’ and ‘not cancel’, etc.?

We haven’t considered doing that, but it makes sense for options to remove the ‘x’/escape.

I’m not quite sure what you mean by ‘cancel’ and ‘not cancel’. I don’t think this made it into update 7, but we are thinking of adding a “run on close actions” checkbox to “close popup” actions, so you could decide whether to skip or run “on close actions”.

I can’t speak for all, but in my use cases and others I’ve seen in the community, there’s a need to run a different set of actions depending on if the user is “cancelling” the dialog or “saving/continuing/etc.” (aka. “not cancelling” - I used this is a generic way of expressing anything that isn’t a cancel, sorry for the confusion).

If ‘X’ and escape were able to be disabled, then the only way a dialog could be ‘closed’ is via a page title button on the dialog (or javascript) which allows the page designer to put action sequences on those buttons and not have to worry that they won’t fire (e.g. as a result of pressing ‘X’ or escape since they wouldn’t be there).

If ‘X’ and ‘Escape’ are still available to the end user, then the page developer needs to be able to differentiate between cancel/save operations with an “close” event because they would likely trigger different action sequences.

For example, if the user presses ‘cancel’/‘x’/‘escape’, the action sequence might cancel models changes while if they press ‘save’/‘continue’ the action sequence might save model changes.  Without a way to differentiate (or ensure that the only way to close a dialog is via a custom button) there is no way to handle the different paths.  Even with “After Close” or “Run on Close”, you wouldn’t be able to do different things in different situations unfortunately.

Hope I’m making sense :slight_smile:

I think the “easy” solution would be to allow ‘X’ and ‘Escape’ to be hidden/disabled. This would likely avoid the need to even use “After Close” and “Run after close” for most use cases because the only way to close is via a button or javascript.

I think the main reason people need an “After Close” or “Run on Close” is because of the different ways a dialog could close (it’s a catchall).  However, without knowing ‘why’ the dialog closed, it’s only somewhat useful.

I think the “ideal” solution would be something like the following:

1) Add “Before Close” events - This would give an opportunity to cancel the close.  For example, in “Before Close” a snippet could be called that would return false if the dialog should NOT close

2) Add Global/Page Level Actions - This would allow the definition of a set of actions and assign a name to them.   Then, where the action framework is available, you could choose to define a sequence or choose a global/page level action sequence by name.  This gives you re-use of action sequences.

With these, you could do something like the following:
Global/Page Level Action Sequence
1) SaveStuff
- Save Models (Account, Contact)
- ReQuery Models (Account, Contact)
2) CancelStuff
- Cancel Models(Account, Contact)

Popup
1) Save Button
- Use Action Sequence SaveStuff
- Close Popup
2) Cancel Button
- Use Action Sequence CancelStuff
- Close Popup
3) Before Close Actions
- Call Snippet which returns false if the dialog should not close
4) After Close Actions
a) When ‘X’ or ‘Escape’ - This would fire only if user presses ‘X’ or ‘Escape’ keys
- Use Action Sequence CancelStuff
b) Final (e.g. analogous to try…catch.finally) Actions - This would fire regardless of how dialog closed but after “When ‘X’ or ‘Escape’” (if applicable) fired
- Can’t think of anything you would need to do here but nice to have the option just in case :slight_smile:



Yeah, this all makes sense. Hopefully we’ll be able to get portions of this into the product in upcoming releases.

Sounds great, thanks Ben!

Hi @Berry, for using this component does all these parameters (oncreate, onopen, onbeforeclose, onclose) are mandatory?

<myns__mynspopupcontroller uniqueid="sk-3roGN_-93" oncreate="onCreateSnippet" onopen="onOpenSnippet" onbeforeclose="onBeforeCloseSnippet" onclose="onCloseSnippet" hideclose="true" disableescape="true"/>

I just want to hide the X symbol on the pop-up and remove escape button actions. So I just put like below and it is not working for me. Let me know if any more changes that I need to put to make it work.

<myns__mynspopupcontroller uniqueid="sk-3roGN_-93" hideclose="true" disableescape="true"/> 

Thanks.

Hello Dinesh -

The oncreate/onopen/etc. should all be optional.  When you say it’s not working for you, can you provide specifics as to what’s not working, any error messages in the browser developer console, etc.

If you can create a full sample page using only stock objects, I’m happy to take a look.

Thanks!