Thursday, December 6, 2012

Register hotkeys for PrimeFaces dialogs

In this blog post I would like to share a jQuery based (low level), fast working solution to register hotkeys for PrimeFaces dialogs. Why do we need this low level solution? Primary because the PrimeFaces component p:hotkey doesn't work as expected in dialogs. We were only interested in two hotkeys ESC and ENTER, so that I will only show a solution for these two hotkeys.

Requirements: When user presses the ESC key, a dialog should be closed. When user presses the ENTER key, the dialog action should be submitted (imagine e.g. a "Are you sure you want to delete?" dialog). Dialog can look like as follows. In the real world it is better to please the dialog into a composite component in order to reuse it.
<p:confirmDialog header="Confirmation" widgetVar="confirmDlgWidget" appendToBody="true">
  <f:facet name="message">
     ...
  </f:facet>
  <h:form>
    <p:commandButton value="OK" process="@this" update="..." action="..." oncomplete="confirmDlgWidget.hide();"/>
    <p:commandButton value="Cancel" onclick="confirmDlgWidget.hide();" type="button"/>
  </h:form>
</p:confirmDialog>
After the dialog tag, the following short JS script should be placed.
<script type="text/javascript">
 /* <![CDATA[ */
 $(function () {
    registerConfirmDialogHotkeys(confirmDlgWidget);
 });
 /* ]]> */
</script>
The JS function itself is located in a file. Here how it was implemented:
function registerConfirmDialogHotkeys(dlgWidget) {

    var key = 'keydown.pfdialog_' + dlgWidget.id;

    $(document).off(key).on(key, function (e) {
        var keyCode = $.ui.keyCode;
        var active = parseInt(dlgWidget.jq.css('z-index')) === PrimeFaces.zindex;

        if (dlgWidget.jq.hasClass('ui-overlay-visible') && active) {
            if (e.which === keyCode.ESCAPE) {
                dlgWidget.hide();
            } else if (e.which === keyCode.ENTER || e.which == keyCode.NUMPAD_ENTER) {
                dlgWidget.jq.find('button:submit:first').click();
                e.preventDefault();
            }
        }
    });
}
The function registers a namespaced keydown JS callback on document. The callback checks if the dialog is active currently. This is normally a last opened dialog. If it is active and the user has pressed ESC, then hide the dialog (widget variable is passed into the function). If it is active and the user has pressed ENTER, then find the first submit button within this dialog and click it to cause an action execution. That's all.

5 comments:

  1. This is very good & useful blog i am regular reader of your blog, thanks for posting wonderful article

    ReplyDelete
  2. How can I fix this to work on Primefaces 5.2?

    ReplyDelete
  3. It's not working on prime 5.2, do you have any idea how could I fix? thanks

    ReplyDelete
    Replies
    1. Thanks for testing it on PF 5.2. I have found the issue. You have to replace

      dlgWidget.jq.hasClass('ui-overlay-visible')

      by

      dlgWidget.jq.is(':visible')

      and it starts to work again!

      Delete
  4. Thank you very much for your amazing work =)

    ReplyDelete

Note: Only a member of this blog may post a comment.