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

  • 6
  • Idea
  • Updated 11 months ago
  • Implemented
  • (Edited)
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.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.$;
skuid.snippet.registerSnippet('onCreateSnippet', function(eventArg) {
   window.alert('onCreateSnippet');
});
skuid.snippet.registerSnippet('onOpenSnippet', function(eventArg) {
   window.alert('onOpenSnippet');
});
skuid.snippet.registerSnippet('onBeforeCloseSnippet', function(eventArg) {
   window.alert('onBeforeCloseSnippet - wasXButton=' + eventArg.wasXButton + ' / wasEscapeKey=' + eventArg.wasEscapeKey);    
   var prohibitClose = eventArg.wasXButton || eventArg.wasEscapeKey;
   // returning false will prohibit close
   return !prohibitClose;
});
skuid.snippet.registerSnippet('onCloseSnippet', function(eventArg) {
   window.alert('onCloseSnippet');
});
})(skuid);</jsitem>
      </javascript>
   </resources>
   <styles>
      <styleitem type="background" bgtype="none"/>
   </styles>
</skuidpage>
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb

Posted 3 years ago

  • 6
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
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.
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
Glad you like it Pat.  Its worked out very well for me, enjoy!
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Thanks Barry.  Really appreciate your sharing here. 
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
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?
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
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.?
Photo of Ben Hubbard

Ben Hubbard, Employee

  • 12,490 Points 10k badge 2x thumb
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".
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
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 :)

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 :)
Photo of Ben Hubbard

Ben Hubbard, Employee

  • 12,490 Points 10k badge 2x thumb
Yeah, this all makes sense. Hopefully we'll be able to get portions of this into the product in upcoming releases.
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
Sounds great, thanks Ben!
Photo of Dinesh Ramanadham

Dinesh Ramanadham

  • 372 Points 250 badge 2x thumb
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.
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,076 Points 10k badge 2x thumb
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!